Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 46 additions & 24 deletions pkg/containers/path_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/aquasecurity/tracee/pkg/bucketscache"
"github.com/aquasecurity/tracee/pkg/errfmt"
"github.com/aquasecurity/tracee/pkg/logger"
"github.com/aquasecurity/tracee/pkg/utils/proc"
)

// ContainerPathResolver generates an accessible absolute path from the root
Expand Down Expand Up @@ -45,36 +46,57 @@ func (cPathRes *ContainerPathResolver) GetHostAbsPath(mountNSAbsolutePath string
pids := cPathRes.mountNSPIDsCache.GetBucket(uint32(mountNS))

for _, pid := range pids {
// cap.SYS_PTRACE is needed here. Instead of raising privileges, since
// this is called too frequently, if the needed event is being traced,
// the needed capabilities are added to the Base ring and are always set
// as effective.
//
// (Note: To change this behavior we need a privileged process/server)

procRootPath := fmt.Sprintf("/proc/%d/root", int(pid))

// fs.FS interface requires relative paths, so the '/' prefix should be trimmed.
entries, err := fs.ReadDir(cPathRes.fs, strings.TrimPrefix(procRootPath, "/"))
procRoot, err := cPathRes.getProcessFSRoot(uint(pid))
if err != nil {
// This process is either not alive or we don't have permissions to access.
// Try next pid in mount ns to find accessible path to mount ns files.
logger.Debugw(
"Finding mount NS path",
"Unreachable proc root path", procRootPath,
"error", err.Error(),
)
logger.Debugw("Could not access process FS", "pid", pid, "error", err)
continue
}
if len(entries) == 0 {
return "", errfmt.Errorf("empty directory")
}
if err == nil {
return fmt.Sprintf("%s%s", procRootPath, mountNSAbsolutePath), nil
}

return fmt.Sprintf("%s%s", procRoot, mountNSAbsolutePath), nil
}

// No PIDs registered in this namespace, or couldn't access FS root of any of the PIDs found.
// Try finding one in procfs.
pid, err := proc.GetAnyProcessInNS("mnt", mountNS)
if err != nil {
// Couldn't find a process in this namespace using procfs
return "", ErrContainerFSUnreachable
}

procRoot, err := cPathRes.getProcessFSRoot(pid)
if err != nil {
logger.Debugw("Could not access process FS", "pid", pid, "error", err)
return "", ErrContainerFSUnreachable
}

// Register this process in the mount namespace
cPathRes.mountNSPIDsCache.AddBucketItem(uint32(mountNS), uint32(pid))

return fmt.Sprintf("%s%s", procRoot, mountNSAbsolutePath), nil
}

func (cPathRes *ContainerPathResolver) getProcessFSRoot(pid uint) (string, error) {
// cap.SYS_PTRACE is needed here. Instead of raising privileges, since
// this is called too frequently, if the needed event is being traced,
// the needed capabilities are added to the Base ring and are always set
// as effective.
//
// (Note: To change this behavior we need a privileged process/server)

procRootPath := fmt.Sprintf("/proc/%d/root", pid)

// fs.FS interface requires relative paths, so the '/' prefix should be trimmed.
entries, err := fs.ReadDir(cPathRes.fs, strings.TrimPrefix(procRootPath, "/"))
if err != nil {
// This process is either not alive or we don't have permissions to access.
return "", errfmt.Errorf("failed accessing process FS root %s: %v", procRootPath, err)
}
if len(entries) == 0 {
return "", errfmt.Errorf("process FS root (%s) is empty", procRootPath)
}

return "", ErrContainerFSUnreachable
return procRootPath, nil
}

var (
Expand Down
8 changes: 0 additions & 8 deletions pkg/ebpf/c/common/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -601,14 +601,6 @@ statfunc int events_perf_submit(program_data_t *p, long ret)
// keep task_info updated
bpf_probe_read_kernel(&p->task_info->context, sizeof(task_context_t), &p->event->context.task);

// Get Stack trace
if (p->config->options & OPT_CAPTURE_STACK_TRACES) {
int stack_id = bpf_get_stackid(p->ctx, &stack_addresses, BPF_F_USER_STACK);
if (stack_id >= 0) {
p->event->context.stack_id = stack_id;
}
}

u32 size = sizeof(event_context_t) + sizeof(u8) +
p->event->args_buf.offset; // context + argnum + arg buffer size

Expand Down
12 changes: 0 additions & 12 deletions pkg/ebpf/c/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,18 +292,6 @@ struct {
__type(value, bool);
} syscall_source_map SEC(".maps");

// store stack traces
#define MAX_STACK_ADDRESSES 1024 // max amount of diff stack trace addrs to buffer

struct stack_addresses {
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
__uint(max_entries, MAX_STACK_ADDRESSES);
__type(key, u32);
__type(value, stack_trace_t); // 1 big byte array of the stack addresses
} stack_addresses SEC(".maps");

typedef struct stack_addresses stack_addresses_t;

// store fds paths by timestamp
struct fd_arg_path_map {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
Expand Down
13 changes: 13 additions & 0 deletions pkg/ebpf/c/stack_unwind/maps.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef __STACK_UNWIND_MAPS_H__
#define __STACK_UNWIND_MAPS_H__

#include <maps.h>

struct stack_unwind_enabled_events {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_EVENT_ID);
__type(key, u32);
__type(value, u32);
} su_enabled_evts SEC(".maps");

#endif /* __STACK_UNWIND_MAPS_H__ */
11 changes: 11 additions & 0 deletions pkg/ebpf/c/stack_unwind/unwind.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifndef __STACK_UNWIND_H__
#define __STACK_UNWIND_H__

#include "maps.h"

statfunc bool stack_trace_enabled_for_event(u32 event_id)
{
return bpf_map_lookup_elem(&su_enabled_evts, &event_id) != NULL;
}

#endif /* __STACK_UNWIND_H__ */
15 changes: 9 additions & 6 deletions pkg/ebpf/c/tracee.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
#include <common/probes.h>
#include <common/signal.h>

#include <stack_unwind/unwind.h>

char LICENSE[] SEC("license") = "GPL";

// trace/events/syscalls.h: TP_PROTO(struct pt_regs *regs, long id)
Expand Down Expand Up @@ -2249,17 +2251,18 @@ int BPF_KPROBE(trace_security_bprm_check)
unsigned long inode_nr = get_inode_nr_from_file(file);
void *file_path = get_path_str(__builtin_preserve_access_index(&file->f_path));

syscall_data_t *sys = &p.task_info->syscall_data;
struct pt_regs *task_regs = get_current_task_pt_regs();

const char *const *argv = NULL;
const char *const *envp = NULL;
switch (sys->id) {
switch (get_current_task_syscall_id()) {
case SYSCALL_EXECVE:
argv = (const char *const *) sys->args.args[1];
envp = (const char *const *) sys->args.args[2];
argv = (const char *const *) get_syscall_arg2(p.event->task, task_regs, false);
envp = (const char *const *) get_syscall_arg3(p.event->task, task_regs, false);
break;
case SYSCALL_EXECVEAT:
argv = (const char *const *) sys->args.args[2];
envp = (const char *const *) sys->args.args[3];
argv = (const char *const *) get_syscall_arg3(p.event->task, task_regs, false);
envp = (const char *const *) get_syscall_arg4(p.event->task, task_regs, false);
break;
default:
break;
Expand Down
46 changes: 46 additions & 0 deletions pkg/ebpf/event_parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type eventParameterHandler func(t *Tracee, eventParams []map[string]filters.Filt
var eventParameterHandlers = map[events.ID]eventParameterHandler{
events.SuspiciousSyscallSource: prepareSuspiciousSyscallSource,
events.StackPivot: prepareStackPivot,
events.StackTrace: populateMapsStackTrace,
}

// handleEventParameters performs initialization actions according to event parameters,
Expand Down Expand Up @@ -157,3 +158,48 @@ func prepareSuspiciousSyscallSource(t *Tracee, eventParams []map[string]filters.
func prepareStackPivot(t *Tracee, eventParams []map[string]filters.Filter[*filters.StringFilter]) error {
return registerSyscallChecker(t, eventParams, "syscall", "stack_pivot_syscalls")
}

func populateMapsStackTrace(t *Tracee, eventParams []map[string]filters.Filter[*filters.StringFilter]) error {
// Get events to produce stack traces for
selectedEvents := map[string]struct{}{}
for _, policyParams := range eventParams {
eventNamesParam, ok := policyParams["events"].(*filters.StringFilter)
if !ok {
return errfmt.Errorf("invalid argument name 'events'")
}
for _, eventName := range eventNamesParam.Equal() {
selectedEvents[eventName] = struct{}{}
}
}

// If "all" was specified, add all events to selectedEvents
if _, found := selectedEvents["all"]; found {
selectedEvents = make(map[string]struct{}, events.MaxCommonID)
for id := range events.MaxCommonID {
d := events.Core.GetDefinitionByID(id)
if d.Valid() {
selectedEvents[d.GetName()] = struct{}{}
}
}
}

logger.Debugw("stack traces are enabled", "selected events", selectedEvents)

// Update selected events map
eventsMap, err := t.bpfModule.GetMap("su_enabled_evts")
if err != nil {
return errfmt.Errorf("could not get BPF map 'su_enabled_evts': %v", err)
}
for event := range selectedEvents {
eventID, found := events.Core.GetDefinitionIDByName(event)
if !found {
return errfmt.Errorf("invalid event %s", event)
}
val := uint32(1)
if err = eventsMap.Update(unsafe.Pointer(&eventID), unsafe.Pointer(&val)); err != nil {
return errfmt.Errorf("failed updating stack unwind events map: %v", err)
}
}

return nil
}
56 changes: 1 addition & 55 deletions pkg/ebpf/events_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,11 @@ package ebpf
import (
"bytes"
"context"
"encoding/binary"
"fmt"
"slices"
"strconv"
"sync"
"unsafe"

"github.com/aquasecurity/tracee/pkg/bufferdecoder"
"github.com/aquasecurity/tracee/pkg/capabilities"
"github.com/aquasecurity/tracee/pkg/errfmt"
"github.com/aquasecurity/tracee/pkg/events"
"github.com/aquasecurity/tracee/pkg/logger"
Expand Down Expand Up @@ -192,12 +188,6 @@ func (t *Tracee) decodeEvents(ctx context.Context, sourceChan chan []byte) (<-ch
continue
}

// Add stack trace if needed
var stackAddresses []uint64
if t.config.Output.StackAddresses {
stackAddresses = t.getStackAddresses(eCtx.StackID)
}

containerInfo := t.containers.GetCgroupInfo(eCtx.CgroupID).Container
containerData := trace.Container{
ID: containerInfo.ContainerId,
Expand Down Expand Up @@ -275,7 +265,7 @@ func (t *Tracee) decodeEvents(ctx context.Context, sourceChan chan []byte) (<-ch
evt.ArgsNum = int(argnum)
evt.ReturnValue = int(eCtx.Retval)
evt.Args = args
evt.StackAddresses = stackAddresses
evt.StackAddresses = nil
evt.ContextFlags = flags
evt.Syscall = syscall
evt.Metadata = nil
Expand Down Expand Up @@ -636,50 +626,6 @@ func (t *Tracee) sinkEvents(ctx context.Context, in <-chan *trace.Event) <-chan
return errc
}

// getStackAddresses returns the stack addresses for a given StackID
func (t *Tracee) getStackAddresses(stackID uint32) []uint64 {
stackAddresses := make([]uint64, maxStackDepth)
stackFrameSize := (strconv.IntSize / 8)

// Lookup the StackID in the map
// The ID could have aged out of the Map, as it only holds a finite number of
// Stack IDs in it's Map
var stackBytes []byte
err := capabilities.GetInstance().EBPF(func() error {
bytes, e := t.StackAddressesMap.GetValue(unsafe.Pointer(&stackID))
if e != nil {
stackBytes = bytes
}
return e
})
if err != nil {
logger.Debugw("failed to get StackAddress", "error", err)
return stackAddresses[0:0]
}

stackCounter := 0
for i := 0; i < len(stackBytes); i += stackFrameSize {
stackAddresses[stackCounter] = 0
stackAddr := binary.LittleEndian.Uint64(stackBytes[i : i+stackFrameSize])
if stackAddr == 0 {
break
}
stackAddresses[stackCounter] = stackAddr
stackCounter++
}

// Attempt to remove the ID from the map so we don't fill it up
// But if this fails continue on
err = capabilities.GetInstance().EBPF(func() error {
return t.StackAddressesMap.DeleteKey(unsafe.Pointer(&stackID))
})
if err != nil {
logger.Debugw("failed to delete stack address from eBPF map", "error", err)
}

return stackAddresses[0:stackCounter]
}

// WaitForPipeline waits for results from all error channels.
func (t *Tracee) WaitForPipeline(errs ...<-chan error) error {
errc := MergeErrors(errs...)
Expand Down
3 changes: 2 additions & 1 deletion pkg/ebpf/processor_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ func processKernelReadFile(event *trace.Event) error {
// processSchedProcessExec processes a sched_process_exec event by capturing the executed file.
func (t *Tracee) processSchedProcessExec(event *trace.Event) error {
// cache this pid by it's mnt ns
// TODO: don't do this if Tracee is not in the root PID NS?
if event.ProcessID == 1 {
t.pidsInMntns.ForceAddBucketItem(uint32(event.MountNS), uint32(event.HostProcessID))
} else {
Expand Down Expand Up @@ -469,7 +470,7 @@ func (t *Tracee) removeContext(event *trace.Event) error {
event.Container = trace.Container{}
event.Kubernetes = trace.Kubernetes{}
event.Syscall = ""
event.StackAddresses = []uint64{}
event.StackAddresses = nil
event.ContextFlags = trace.ContextFlags{}
event.ThreadEntityId = 0
event.ProcessEntityId = 0
Expand Down
12 changes: 1 addition & 11 deletions pkg/ebpf/tracee.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ type Tracee struct {
defaultProbes *probes.ProbeGroup
extraProbes map[string]*probes.ProbeGroup
// BPF Maps
StackAddressesMap *bpf.BPFMap
FDArgPathMap *bpf.BPFMap
FDArgPathMap *bpf.BPFMap
// Perf Buffers
eventsPerfMap *bpf.PerfBuffer // perf buffer for events
fileWrPerfMap *bpf.PerfBuffer // perf buffer for file writes
Expand Down Expand Up @@ -503,15 +502,6 @@ func (t *Tracee) Init(ctx gocontext.Context) error {
return errfmt.Errorf("error initializing network capture: %v", err)
}

// Get reference to stack trace addresses map

stackAddressesMap, err := t.bpfModule.GetMap("stack_addresses")
if err != nil {
t.Close()
return errfmt.Errorf("error getting access to 'stack_addresses' eBPF Map %v", err)
}
t.StackAddressesMap = stackAddressesMap

// Get reference to fd arg path map

fdArgPathMap, err := t.bpfModule.GetMap("fd_arg_path_map")
Expand Down
Loading