From 01399d6f9328e8c052dc4f48d5e642c0fc703366 Mon Sep 17 00:00:00 2001 From: Ofek Shaked Date: Wed, 5 Jun 2024 13:02:41 +0300 Subject: [PATCH] Bugfix: 32-bit compat program false positive 32-bit programs on x86-64 may use a fast syscall method, whose code resides in the VDSO VMA. This VMA is not file backed, so it was incorrectly detected as anonymous memory. --- pkg/ebpf/c/common/common.h | 4 ++-- pkg/ebpf/c/common/memory.h | 12 ++++++++++++ pkg/ebpf/c/tracee.bpf.c | 3 ++- pkg/ebpf/c/vmlinux.h | 12 ++++++++++++ pkg/ebpf/event_filters.go | 2 +- pkg/ebpf/initialization/kconfig.go | 5 ++--- 6 files changed, 31 insertions(+), 7 deletions(-) diff --git a/pkg/ebpf/c/common/common.h b/pkg/ebpf/c/common/common.h index b2f16661a660..a795e2482b56 100644 --- a/pkg/ebpf/c/common/common.h +++ b/pkg/ebpf/c/common/common.h @@ -33,7 +33,7 @@ statfunc const char *get_device_name(struct device *dev) #define has_prefix(p, s, n) \ ({ \ int rc = 0; \ - char *pre = p, *str = s; \ + const char *pre = p, *str = s; \ _Pragma("unroll") for (int z = 0; z < n; pre++, str++, z++) \ { \ if (!*pre) { \ @@ -49,7 +49,7 @@ statfunc const char *get_device_name(struct device *dev) #else -static __inline int has_prefix(char *prefix, char *str, int n) +static __inline int has_prefix(const char *prefix, const char *str, int n) { int i; #pragma unroll diff --git a/pkg/ebpf/c/common/memory.h b/pkg/ebpf/c/common/memory.h index d8a7aca2375b..effeb80a2592 100644 --- a/pkg/ebpf/c/common/memory.h +++ b/pkg/ebpf/c/common/memory.h @@ -18,6 +18,8 @@ statfunc unsigned long get_vma_start(struct vm_area_struct *); 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); +statfunc bool vma_is_anon(struct vm_area_struct *vma); +statfunc bool vma_is_vdso(struct vm_area_struct *vma); // FUNCTIONS @@ -156,4 +158,14 @@ statfunc bool vma_is_anon(struct vm_area_struct *vma) return BPF_CORE_READ(vma, vm_file) == NULL; } +statfunc bool vma_is_vdso(struct vm_area_struct *vma) +{ + struct vm_special_mapping *special_mapping = + (struct vm_special_mapping *) BPF_CORE_READ(vma, vm_private_data); + if (special_mapping == NULL) + return false; + + return has_prefix("[vdso]", BPF_CORE_READ(special_mapping, name), 6) == 0; +} + #endif diff --git a/pkg/ebpf/c/tracee.bpf.c b/pkg/ebpf/c/tracee.bpf.c index e40a18d722cb..b3f9325dcfd0 100644 --- a/pkg/ebpf/c/tracee.bpf.c +++ b/pkg/ebpf/c/tracee.bpf.c @@ -5117,8 +5117,9 @@ statfunc enum vma_type get_vma_type(struct vm_area_struct *vma) if (vma_is_heap(vma)) return VMA_HEAP; - if (vma_is_anon(vma)) + if (vma_is_anon(vma) && !vma_is_vdso(vma)) { return VMA_ANON; + } return VMA_OTHER; } diff --git a/pkg/ebpf/c/vmlinux.h b/pkg/ebpf/c/vmlinux.h index 6fde9b9fc855..e6ab04626170 100644 --- a/pkg/ebpf/c/vmlinux.h +++ b/pkg/ebpf/c/vmlinux.h @@ -289,6 +289,16 @@ struct rb_node { struct rb_node *rb_left; } __attribute__((aligned(sizeof(long)))); +struct vm_area_struct; + +struct vm_operations_struct { + const char *(*name)(struct vm_area_struct *vma); +}; + +struct vm_special_mapping { + const char *name; +}; + struct vm_area_struct { union { struct { @@ -299,7 +309,9 @@ struct vm_area_struct { struct rb_node vm_rb; struct mm_struct *vm_mm; long unsigned int vm_flags; + const struct vm_operations_struct *vm_ops; struct file *vm_file; + void *vm_private_data; }; typedef unsigned int __kernel_gid32_t; diff --git a/pkg/ebpf/event_filters.go b/pkg/ebpf/event_filters.go index 5cbf6e2d99a8..21ca604f17b2 100644 --- a/pkg/ebpf/event_filters.go +++ b/pkg/ebpf/event_filters.go @@ -27,7 +27,7 @@ func (t *Tracee) populateEventFilterMaps() error { eventFilters := map[string]filters.Filter[*filters.StringFilter]{} for it := t.config.Policies.CreateAllIterator(); it.HasNext(); { p := it.Next() - f := p.ArgFilter.GetEventFilters(eventID) + f := p.DataFilter.GetEventFilters(eventID) if len(f) == 0 { continue } diff --git a/pkg/ebpf/initialization/kconfig.go b/pkg/ebpf/initialization/kconfig.go index 7690496dd4d3..d86e06d9016c 100644 --- a/pkg/ebpf/initialization/kconfig.go +++ b/pkg/ebpf/initialization/kconfig.go @@ -1,7 +1,6 @@ package initialization import ( - "github.com/aquasecurity/libbpfgo/helpers" "github.com/aquasecurity/tracee/pkg/errfmt" "github.com/aquasecurity/tracee/pkg/logger" "github.com/aquasecurity/tracee/pkg/utils/environment" @@ -11,7 +10,7 @@ import ( // Add here all kconfig variables used within tracee.bpf.c const ( CONFIG_ARCH_HAS_SYSCALL_WRAPPER environment.KernelConfigOption = iota + environment.CUSTOM_OPTION_START - CONFIG_MMU helpers.KernelConfigOption = iota + helpers.CUSTOM_OPTION_START + CONFIG_MMU environment.KernelConfigOption = iota + environment.CUSTOM_OPTION_START ) var kconfigUsed = map[environment.KernelConfigOption]string{ @@ -36,7 +35,7 @@ func LoadKconfigValues(kc *environment.KernelConfig) (map[environment.KernelConf values[key] = environment.UNDEFINED } values[CONFIG_ARCH_HAS_SYSCALL_WRAPPER] = environment.BUILTIN // assume CONFIG_ARCH_HAS_SYSCALL_WRAPPER is a BUILTIN option - values[CONFIG_MMU] = helpers.BUILTIN // assume CONFIG_MMU is a BUILTIN option + values[CONFIG_MMU] = environment.BUILTIN // assume CONFIG_MMU is a BUILTIN option } else { for key := range kconfigUsed { values[key] = kc.GetValue(key) // undefined, builtin OR module