diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index e6e1cab2f18d..d177d146ed4c 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -118,6 +118,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - system module: Fix panic during initialisation when /proc/stat can't be read. {pull}17569[17569] - system/package: Fix an error that can occur while trying to persist package metadata. {issue}18536[18536] {pull}18887[18887] - system/socket: Fix bugs leading to wrong process being attributed to flows. {pull}29166[29166] {issue}17165[17165] +- system/socket: Fix process name and arg truncation for long names, paths and args lists. {issue}24667[24667] {pull}29410[29410] *Filebeat* diff --git a/x-pack/auditbeat/module/system/socket/events.go b/x-pack/auditbeat/module/system/socket/events.go index d0c37701d541..74b31cc7e7f3 100644 --- a/x-pack/auditbeat/module/system/socket/events.go +++ b/x-pack/auditbeat/module/system/socket/events.go @@ -859,8 +859,10 @@ func (e *inetReleaseCall) Update(s *state) error { // kernels it needs to dump fixed-size arrays in 8-byte chunks. As the total // number of fetchargs available is limited, we have to dump only the first // 128 bytes of every argument. -const maxProgArgLen = 128 -const maxProgArgs = 5 +const ( + maxProgArgLen = 128 + maxProgArgs = 5 +) type execveCall struct { Meta tracing.Metadata `kprobe:"metadata"` @@ -880,38 +882,78 @@ type execveCall struct { func (e *execveCall) getProcess() *process { p := &process{ pid: e.Meta.PID, - path: readCString(e.Path[:]), created: kernelTime(e.Meta.Timestamp), } - p.name = filepath.Base(p.path) - var argc int - for argc = 0; argc <= maxProgArgs; argc++ { - if e.Ptrs[argc] == 0 { - break + + if idx := bytes.IndexByte(e.Path[:], 0); idx >= 0 { + // Fast path if we already have the path. + p.path = string(e.Path[:idx]) + } else { + // Attempt to get the path from the /prox//exe symlink. + var err error + p.path, err = filepath.EvalSymlinks(fmt.Sprintf("/proc/%d/exe", e.Meta.PID)) + if err != nil { + if pe, ok := err.(*os.PathError); ok && strings.Contains(pe.Path, "(deleted)") { + // Keep the deleted path from the PathError. + p.path = pe.Path + } else { + // Fallback to the truncated path. + p.path = string(e.Path[:]) + " ..." + } } } - p.args = make([]string, argc) - params := [maxProgArgs][]byte{ + + // Check for truncation of arg list or arguments. + params := [...][]byte{ e.Param0[:], e.Param1[:], e.Param2[:], e.Param3[:], e.Param4[:], } - limit := argc - if limit > maxProgArgs { - limit = maxProgArgs - p.args[limit] = "..." + var ( + argc int + truncatedArg bool + ) + for argc = 0; argc < len(e.Ptrs); argc++ { + if e.Ptrs[argc] == 0 { + break + } + if argc < len(params) && bytes.IndexByte(params[argc], 0) < 0 { + truncatedArg = true + } + } + if argc > maxProgArgs || truncatedArg { + // Attempt to get complete args list from /proc//cmdline. + cmdline, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", e.Meta.PID)) + if err == nil { + p.args = strings.Split(strings.TrimRight(string(cmdline), "\x00"), "\x00") + } } - for i := 0; i < limit; i++ { - p.args[i] = readCString(params[i]) + + if p.args == nil { + // Fallback to arg list if unsuccessful or no truncation. + p.args = make([]string, argc) + if argc > maxProgArgs { + argc = maxProgArgs + p.args[argc] = "..." + } + for i, par := range params[:argc] { + p.args[i] = readCString(par) + } } - if p.hasCreds = e.creds != nil; p.hasCreds { + + // Get name from first argument. + p.name = filepath.Base(p.args[0]) + + if e.creds != nil { + p.hasCreds = true p.uid = e.creds.UID p.gid = e.creds.GID p.euid = e.creds.EUID p.egid = e.creds.EGID } + return p } diff --git a/x-pack/auditbeat/module/system/socket/socket_linux.go b/x-pack/auditbeat/module/system/socket/socket_linux.go index e7f1a059dd1e..fb66f717d843 100644 --- a/x-pack/auditbeat/module/system/socket/socket_linux.go +++ b/x-pack/auditbeat/module/system/socket/socket_linux.go @@ -187,6 +187,15 @@ func (m *MetricSet) Run(r mb.PushReporterV2) { } else { for _, p := range procs { if i, err := p.Info(); err == nil { + if len(i.Name) == 16 && len(i.Args) != 0 { + // github.com/prometheus/procfs uses /proc//stat for + // the process name which is truncated to 16 bytes, so get + // the name from the cmdline data if it might be truncated. + // The guard for length of i.Args is for cases where there + // is no command line reported by proc fs; this should never + // happen, but does. + i.Name = filepath.Base(i.Args[0]) + } process := &process{ name: i.Name, pid: uint32(i.PID),