diff --git a/pkg/ebpf/c/common/kconfig.h b/pkg/ebpf/c/common/kconfig.h index d28893496d1d..e0e53f5bbe43 100644 --- a/pkg/ebpf/c/common/kconfig.h +++ b/pkg/ebpf/c/common/kconfig.h @@ -9,7 +9,8 @@ enum kconfig_key_e { - ARCH_HAS_SYSCALL_WRAPPER = 1000U + ARCH_HAS_SYSCALL_WRAPPER = 1000U, + MMU = 1001U }; // PROTOTYPES diff --git a/pkg/ebpf/c/common/memory.h b/pkg/ebpf/c/common/memory.h index 3f8142b2b057..d8a7aca2375b 100644 --- a/pkg/ebpf/c/common/memory.h +++ b/pkg/ebpf/c/common/memory.h @@ -4,13 +4,10 @@ #include #include +#include // PROTOTYPES -typedef long (*vma_callback_fn)(struct task_struct *task, - struct vm_area_struct *vma, - void *callback_ctx); - statfunc struct mm_struct *get_mm_from_task(struct task_struct *); statfunc unsigned long get_arg_start_from_mm(struct mm_struct *); statfunc unsigned long get_arg_end_from_mm(struct mm_struct *); @@ -18,7 +15,7 @@ statfunc unsigned long get_env_start_from_mm(struct mm_struct *); statfunc unsigned long get_env_end_from_mm(struct mm_struct *); statfunc unsigned long get_vma_flags(struct vm_area_struct *); statfunc unsigned long get_vma_start(struct vm_area_struct *); -statfunc void find_vma(struct task_struct *task, u64 addr, vma_callback_fn cb_fn, void *cb_ctx); +statfunc struct vm_area_struct *find_vma(struct task_struct *task, u64 addr); statfunc bool vma_is_stack(struct vm_area_struct *vma); statfunc bool vma_is_heap(struct vm_area_struct *vma); @@ -75,42 +72,26 @@ statfunc unsigned long get_vma_start(struct vm_area_struct *vma) */ #define MAX_VMA_RB_TREE_DEPTH 25 -/** - * Given a task, find the first VMA which contains the given address, - * and call the specified callback function with the found VMA - * and the specified context. - * A callback function is required becuase this function potentially uses - * bpf_find_vma(), which requires a callback function. - * - * A generic callback function which receives a `struct vm_area_struct **` - * as its context and saves the found VMA to it is available in the main - * eBPF source file (tracee.bpf.c:find_vma_callback). - * - * See the check_syscall_source function for a usage example. - * - * DISCLAIMER: on systems with no MMU, multiple VMAs may contain the same address. - * Be aware that this function will call the callback only for the first VMA it finds. - */ -statfunc void find_vma(struct task_struct *task, u64 addr, vma_callback_fn cb_fn, void *cb_ctx) +// Given a task, find the first VMA which contains the given address. +statfunc struct vm_area_struct *find_vma(struct task_struct *task, u64 addr) { /** - * From kernel version 6.1, the data structure with which VMAs + * TODO: from kernel version 6.1, the data structure with which VMAs * are managed changed from an RB tree to a maple tree. - * In version 5.17 the "bpf_find_vma" helper was added. - * This means that if the helper does not exist, we can assume - * that the RB tree structure is used. + * We currently don't support finding VMAs on such systems. */ - - if (bpf_core_enum_value_exists(enum bpf_func_id, BPF_FUNC_find_vma)) { - bpf_find_vma(task, addr, cb_fn, cb_ctx, 0); - return; + struct mm_struct *mm = BPF_CORE_READ(task, mm); + if (!bpf_core_field_exists(mm->mm_rb)) + return NULL; + + // TODO: we don't support NOMMU systems yet (looking up VMAs on them requires walking the VMA + // linked list) + if (!get_kconfig(MMU)) { + return NULL; } - // bpf_find_vma doesn't exist, we can assume the VMAs are stored in an RB tree. - // This logic is based on the find_vma() function in mm/mmap.c - struct vm_area_struct *vma = NULL; - struct rb_node *rb_node = BPF_CORE_READ(task, mm->mm_rb.rb_node); + struct rb_node *rb_node = BPF_CORE_READ(mm, mm_rb.rb_node); #pragma unroll for (int i = 0; i < MAX_VMA_RB_TREE_DEPTH; i++) { @@ -132,8 +113,7 @@ statfunc void find_vma(struct task_struct *task, u64 addr, vma_callback_fn cb_fn rb_node = BPF_CORE_READ(rb_node, rb_right); } - if (vma != NULL) - cb_fn(task, vma, cb_ctx); + return vma; } statfunc bool vma_is_stack(struct vm_area_struct *vma) diff --git a/pkg/ebpf/c/tracee.bpf.c b/pkg/ebpf/c/tracee.bpf.c index 23d4936644ec..e797a5d01566 100644 --- a/pkg/ebpf/c/tracee.bpf.c +++ b/pkg/ebpf/c/tracee.bpf.c @@ -5161,13 +5161,6 @@ statfunc enum vma_type get_vma_type(struct vm_area_struct *vma) return VMA_OTHER; } -static long find_vma_callback(struct task_struct *task, struct vm_area_struct *vma, void *ctx) -{ - struct vm_area_struct **pvma = (struct vm_area_struct **) ctx; - *pvma = vma; - return 0; -} - SEC("raw_tracepoint/check_syscall_source") int check_syscall_source(struct bpf_raw_tracepoint_args *ctx) { @@ -5192,10 +5185,6 @@ int check_syscall_source(struct bpf_raw_tracepoint_args *ctx) if (!should_submit(CHECK_SYSCALL_SOURCE, p.event)) goto out; - struct task_struct *task_btf = bpf_get_current_task_btf(); - if (task_btf == NULL) - goto out; - // Get instruction pointer struct pt_regs *regs = (struct pt_regs *) ctx->args[0]; #if defined(bpf_target_x86) @@ -5205,8 +5194,7 @@ int check_syscall_source(struct bpf_raw_tracepoint_args *ctx) #endif // Find VMA which contains the instruction pointer - struct vm_area_struct *vma = NULL; - find_vma(task_btf, ip, find_vma_callback, &vma); + struct vm_area_struct *vma = find_vma(task, ip); if (vma == NULL) goto out; diff --git a/pkg/ebpf/event_filters.go b/pkg/ebpf/event_filters.go index d23f6ead6dbf..6d373cb4aa48 100644 --- a/pkg/ebpf/event_filters.go +++ b/pkg/ebpf/event_filters.go @@ -40,7 +40,7 @@ func (t *Tracee) populateEventFilterMaps() error { err := handler(eventFilters, t.bpfModule) if err != nil { logger.Errorw("Failed to handle event filter for event " + events.Core.GetDefinitionByID(eventID).GetName() + ", err: " + err.Error()) - t.cancelEventFromEventState(eventID) + t.eventsDependencies.RemoveEvent(eventID) } } return nil diff --git a/pkg/ebpf/initialization/kconfig.go b/pkg/ebpf/initialization/kconfig.go index 3a9ad31ba4b7..b9d0bbb0909d 100644 --- a/pkg/ebpf/initialization/kconfig.go +++ b/pkg/ebpf/initialization/kconfig.go @@ -11,10 +11,12 @@ import ( // Add here all kconfig variables used within tracee.bpf.c const ( CONFIG_ARCH_HAS_SYSCALL_WRAPPER helpers.KernelConfigOption = iota + helpers.CUSTOM_OPTION_START + CONFIG_MMU helpers.KernelConfigOption = iota + helpers.CUSTOM_OPTION_START ) var kconfigUsed = map[helpers.KernelConfigOption]string{ CONFIG_ARCH_HAS_SYSCALL_WRAPPER: "CONFIG_ARCH_HAS_SYSCALL_WRAPPER", + CONFIG_MMU: "CONFIG_MMU", } // LoadKconfigValues load all kconfig variables used within tracee.bpf.c @@ -34,6 +36,7 @@ func LoadKconfigValues(kc *helpers.KernelConfig) (map[helpers.KernelConfigOption values[key] = helpers.UNDEFINED } values[CONFIG_ARCH_HAS_SYSCALL_WRAPPER] = helpers.BUILTIN // assume CONFIG_ARCH_HAS_SYSCALL_WRAPPER is a BUILTIN option + values[CONFIG_MMU] = helpers.BUILTIN // assume CONFIG_MMU is a BUILTIN option } else { for key := range kconfigUsed { values[key] = kc.GetValue(key) // undefined, builtin OR module diff --git a/tests/e2e-inst-signatures/e2e-check_syscall_source.go b/tests/e2e-inst-signatures/e2e-check_syscall_source.go index b328f79726fa..1ef649db97b0 100644 --- a/tests/e2e-inst-signatures/e2e-check_syscall_source.go +++ b/tests/e2e-inst-signatures/e2e-check_syscall_source.go @@ -3,6 +3,8 @@ package main import ( "fmt" + libbfgo "github.com/aquasecurity/libbpfgo/helpers" + "github.com/aquasecurity/tracee/signatures/helpers" "github.com/aquasecurity/tracee/types/detect" "github.com/aquasecurity/tracee/types/protocol" @@ -11,6 +13,7 @@ import ( type e2eCheckSyscallSource struct { cb detect.SignatureHandler + hasMapleTree bool foundStack bool foundHeap bool foundAnonVma bool @@ -18,6 +21,20 @@ type e2eCheckSyscallSource struct { func (sig *e2eCheckSyscallSource) Init(ctx detect.SignatureContext) error { sig.cb = ctx.Callback + + // Find if this system uses maple trees to manage VMAs. + // If so we don't expect any check_syscall_source event to be submitted. + ksyms, err := libbfgo.NewKernelSymbolTable() + if err != nil { + return err + } + _, err = ksyms.GetSymbolByName("mt_find") + if err != nil { + sig.hasMapleTree = false + } else { + sig.hasMapleTree = true + } + return nil } @@ -35,6 +52,7 @@ func (sig *e2eCheckSyscallSource) GetMetadata() (detect.SignatureMetadata, error func (sig *e2eCheckSyscallSource) GetSelectedEvents() ([]detect.SignatureEventSelector, error) { return []detect.SignatureEventSelector{ {Source: "tracee", Name: "check_syscall_source"}, + {Source: "tracee", Name: "init_namespaces"}, // This event always happens so we can pass the test on unsupported kernels }, nil } @@ -45,6 +63,19 @@ func (sig *e2eCheckSyscallSource) OnEvent(event protocol.Event) error { } switch eventObj.EventName { + case "init_namespaces": + // If the system uses maple trees we won't get any check_syscall_source events, pass the test + if sig.hasMapleTree { + m, _ := sig.GetMetadata() + + sig.cb(&detect.Finding{ + SigMetadata: m, + Event: event, + Data: map[string]interface{}{}, + }) + + return nil + } case "check_syscall_source": syscall, err := helpers.GetTraceeStringArgumentByName(eventObj, "syscall") if err != nil {