Skip to content

Commit

Permalink
proc_reader: handle in_init_tree
Browse files Browse the repository at this point in the history
Recently we introduced the in_init_tree flag into execve map values to indicate whether
a process is a member of the initial process tree for a container. This worked well for
containers started after Tetragon, but broke for cases where the container was started
before Tetragon, since our procfs walk did not account for the in_init_tree flag. Fix this
behaviour by introducing logic in the procfs walk to account for this.

Signed-off-by: William Findlay <[email protected]>
  • Loading branch information
will-isovalent committed Jan 28, 2025
1 parent 1dc87c6 commit 503cab7
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 25 deletions.
68 changes: 43 additions & 25 deletions pkg/sensors/exec/procevents/proc_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
package procevents

import (
"cmp"
"fmt"
"os"
"path/filepath"
"regexp"
"slices"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -320,6 +322,41 @@ func updateExecveMapStats(procs int64) {
}
}

func procToKeyValue(p procs, inInitTree map[uint32]struct{}) (*execvemap.ExecveKey, *execvemap.ExecveValue) {
k := &execvemap.ExecveKey{Pid: p.pid}
v := &execvemap.ExecveValue{}

v.Parent.Pid = p.ppid
v.Parent.Ktime = p.pktime
v.Process.Pid = p.pid
v.Process.Ktime = p.ktime
v.Flags = 0
v.Nspid = p.nspid
v.Capabilities.Permitted = p.permitted
v.Capabilities.Effective = p.effective
v.Capabilities.Inheritable = p.inheritable
v.Namespaces.UtsInum = p.uts_ns
v.Namespaces.IpcInum = p.ipc_ns
v.Namespaces.MntInum = p.mnt_ns
v.Namespaces.PidInum = p.pid_ns
v.Namespaces.PidChildInum = p.pid_for_children_ns
v.Namespaces.NetInum = p.net_ns
v.Namespaces.TimeInum = p.time_ns
v.Namespaces.TimeChildInum = p.time_for_children_ns
v.Namespaces.CgroupInum = p.cgroup_ns
v.Namespaces.UserInum = p.user_ns
pathLength := copy(v.Binary.Path[:], p.exe)
v.Binary.PathLength = int32(pathLength)

_, parentInInitTree := inInitTree[p.ppid]
if v.Nspid == 1 || parentInInitTree {
v.Flags |= api.EventInInitTree
inInitTree[p.pid] = struct{}{}
}

return k, v
}

func writeExecveMap(procs []procs) {
mapDir := bpf.MapPrefixPath()

Expand All @@ -335,32 +372,9 @@ func writeExecveMap(procs []procs) {
panic(err)
}
}
inInitTree := make(map[uint32]struct{})
for _, p := range procs {
k := &execvemap.ExecveKey{Pid: p.pid}
v := &execvemap.ExecveValue{}

v.Parent.Pid = p.ppid
v.Parent.Ktime = p.pktime
v.Process.Pid = p.pid
v.Process.Ktime = p.ktime
v.Flags = 0
v.Nspid = p.nspid
v.Capabilities.Permitted = p.permitted
v.Capabilities.Effective = p.effective
v.Capabilities.Inheritable = p.inheritable
v.Namespaces.UtsInum = p.uts_ns
v.Namespaces.IpcInum = p.ipc_ns
v.Namespaces.MntInum = p.mnt_ns
v.Namespaces.PidInum = p.pid_ns
v.Namespaces.PidChildInum = p.pid_for_children_ns
v.Namespaces.NetInum = p.net_ns
v.Namespaces.TimeInum = p.time_ns
v.Namespaces.TimeChildInum = p.time_for_children_ns
v.Namespaces.CgroupInum = p.cgroup_ns
v.Namespaces.UserInum = p.user_ns
pathLength := copy(v.Binary.Path[:], p.exe)
v.Binary.PathLength = int32(pathLength)

k, v := procToKeyValue(p, inInitTree)
err := m.Put(k, v)
if err != nil {
logger.GetLogger().WithField("value", v).WithError(err).Warn("failed to put value in execve_map")
Expand Down Expand Up @@ -647,6 +661,10 @@ func listRunningProcs(procPath string) ([]procs, error) {

logger.GetLogger().Infof("Read ProcFS %s appended %d/%d entries", option.Config.ProcFS, len(processes), len(procFS))

slices.SortFunc(processes, func(a, b procs) int {
return cmp.Compare(a.pid, b.pid)
})

return processes, nil
}

Expand Down
39 changes: 39 additions & 0 deletions pkg/sensors/exec/procevents/proc_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@
package procevents

import (
"os/exec"
"strconv"
"strings"
"testing"
"time"

"github.com/cilium/tetragon/pkg/api"
"github.com/cilium/tetragon/pkg/observer/observertesthelper/docker"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand All @@ -20,3 +27,35 @@ func TestListRunningProcs(t *testing.T) {
require.Equal(t, p.pid, p.tid)
}
}

func TestInInitTreeProcfs(t *testing.T) {
if err := exec.Command("docker", "version").Run(); err != nil {
t.Skipf("docker not available. skipping test: %s", err)
}

containerID := docker.Create(t, "--name", "procfs-in-init-tree-test", "bash", "bash", "-c", "sleep infinity")

docker.Start(t, "procfs-in-init-tree-test")
time.Sleep(1 * time.Second)

rootPidOutput, err := exec.Command("docker", "inspect", "-f", "{{.State.Pid}}", containerID).Output()
require.NoError(t, err, "root pid should fetch")
rootPid, err := strconv.Atoi(strings.TrimSpace(string(rootPidOutput)))
require.NoError(t, err, "root pid should parse")

procs, err := listRunningProcs("/proc")
require.NoError(t, err)
require.NotNil(t, procs)
require.NotEqual(t, 0, len(procs))

inInitTree := make(map[uint32]struct{})
for _, p := range procs {
require.NotZero(t, p.pid)
require.Equal(t, p.pid, p.tid)
_, v := procToKeyValue(p, inInitTree)
if v.Process.Pid == uint32(rootPid) || v.Parent.Pid == uint32(rootPid) {
isInInitTree := v.Flags&api.EventInInitTree == api.EventInInitTree
assert.True(t, isInInitTree)
}
}
}

0 comments on commit 503cab7

Please sign in to comment.