From 03d7be933f2259b5226d77575a68dfafa0a66c10 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. --- go.mod | 1 + go.sum | 4 ++-- 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 ++--- .../e2e-inst-signatures/e2e-check_syscall_source.go | 6 +++--- 9 files changed, 37 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 197400758532..a2e57c53b05d 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/IBM/fluent-forward-go v0.2.1 github.com/Masterminds/sprig/v3 v3.2.3 github.com/aquasecurity/libbpfgo v0.7.0-libbpf-1.4 + github.com/aquasecurity/libbpfgo/helpers v0.4.5 github.com/aquasecurity/tracee/api v0.0.0-20240531131043-a237ddf7b190 github.com/aquasecurity/tracee/signatures/helpers v0.0.0-20240404084547-37dc481cd60c github.com/aquasecurity/tracee/types v0.0.0-20240122122429-7f84f526758d diff --git a/go.sum b/go.sum index b208031c3e80..bf2ae2cf1454 100644 --- a/go.sum +++ b/go.sum @@ -25,10 +25,10 @@ github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRB github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/aquasecurity/libbpfgo v0.7.0-libbpf-1.4 h1:rQ94U12Xlz2tncE8Rxnw3vpp/9hgUIEu3/Lv0/XQM0Q= github.com/aquasecurity/libbpfgo v0.7.0-libbpf-1.4/go.mod h1:iI7QCIZ3kXG0MR+FHsDZck6cYs1y1HyZP3sMObBg0sk= +github.com/aquasecurity/libbpfgo/helpers v0.4.5 h1:eCoLclL3yqv4N9jqGL3T/ckrLPms2r13C4V2xtU75yc= +github.com/aquasecurity/libbpfgo/helpers v0.4.5/go.mod h1:j/TQLmsZpOIdF3CnJODzYngG4yu1YoDCoRMELxkQSSA= github.com/aquasecurity/tracee/api v0.0.0-20240531131043-a237ddf7b190 h1:NJ69oeaA2kcRxrt7YFHVrUHujXCtiee8Re69npcbmHk= github.com/aquasecurity/tracee/api v0.0.0-20240531131043-a237ddf7b190/go.mod h1:jXLAr/iFkfaNTuNcdbx2blngdMD/qaAfxQe9rCL9jwk= -github.com/aquasecurity/tracee/signatures/helpers v0.0.0-20240122160245-67dec940088c h1:Gms5lUHPIq+OpI5HjcZ+l0NZHhSwBd/47nyUZY89c+M= -github.com/aquasecurity/tracee/signatures/helpers v0.0.0-20240122160245-67dec940088c/go.mod h1:SSh6X96P8pT/9B6eBl6ptBo8QnaSCNCZHMOZ1iXyPUw= github.com/aquasecurity/tracee/signatures/helpers v0.0.0-20240404084547-37dc481cd60c h1:bmv9GWkpNlqVMDR8z6GuntbQvr5U7o76bWKZ+RNXK+I= github.com/aquasecurity/tracee/signatures/helpers v0.0.0-20240404084547-37dc481cd60c/go.mod h1:SSh6X96P8pT/9B6eBl6ptBo8QnaSCNCZHMOZ1iXyPUw= github.com/aquasecurity/tracee/types v0.0.0-20240122122429-7f84f526758d h1:6CQjy5G6Cj/VKm8RP1uZnBZxDgfyGo15HfWFnYrkGro= 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 diff --git a/tests/e2e-inst-signatures/e2e-check_syscall_source.go b/tests/e2e-inst-signatures/e2e-check_syscall_source.go index 1ef649db97b0..3a198754ee89 100644 --- a/tests/e2e-inst-signatures/e2e-check_syscall_source.go +++ b/tests/e2e-inst-signatures/e2e-check_syscall_source.go @@ -3,7 +3,7 @@ package main import ( "fmt" - libbfgo "github.com/aquasecurity/libbpfgo/helpers" + libbpfgo "github.com/aquasecurity/libbpfgo/helpers" "github.com/aquasecurity/tracee/signatures/helpers" "github.com/aquasecurity/tracee/types/detect" @@ -24,11 +24,11 @@ func (sig *e2eCheckSyscallSource) Init(ctx detect.SignatureContext) error { // 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() + ksyms, err := libbpfgo.NewKernelSymbolsMap() if err != nil { return err } - _, err = ksyms.GetSymbolByName("mt_find") + _, err = ksyms.GetSymbolByName("system", "mt_find") if err != nil { sig.hasMapleTree = false } else {