From 9376b569c8a64a57de2916b427ea3447559cd098 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 | 2 ++ pkg/ebpf/c/common/common.h | 2 +- pkg/ebpf/c/common/memory.h | 15 +++++++++++++++ pkg/ebpf/c/tracee.bpf.c | 3 ++- pkg/ebpf/c/vmlinux.h | 12 ++++++++++++ pkg/ebpf/initialization/kconfig.go | 5 ++--- .../e2e-check_syscall_source.go | 6 +++--- 8 files changed, 38 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 3031defe94c5..8d76a8a21043 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/IBM/fluent-forward-go v0.2.2 github.com/Masterminds/sprig/v3 v3.2.3 github.com/aquasecurity/libbpfgo v0.7.0-libbpf-1.4.0.20240729111821-61d531acf4ca + github.com/aquasecurity/libbpfgo/helpers v0.4.5 github.com/aquasecurity/tracee/api v0.0.0-20240905132323-d1eaeef6a19f github.com/aquasecurity/tracee/signatures/helpers v0.0.0-20240607205742-90c301111aee github.com/aquasecurity/tracee/types v0.0.0-20240607205742-90c301111aee diff --git a/go.sum b/go.sum index ad9ae14fe212..230543e344d8 100644 --- a/go.sum +++ b/go.sum @@ -406,6 +406,8 @@ github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVb github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aquasecurity/libbpfgo v0.7.0-libbpf-1.4.0.20240729111821-61d531acf4ca h1:OPbvwFFvR11c1bgOLhBq1R5Uk3hwUjHW2KfrdyJan9Y= github.com/aquasecurity/libbpfgo v0.7.0-libbpf-1.4.0.20240729111821-61d531acf4ca/go.mod h1:UpO6kTehEgAGGKR2twztBxvzjTiLiV/cb2xmlYb+TfE= +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-20240905132323-d1eaeef6a19f h1:O4UmMQViaaP1wKL1eXe7C6VylwrUmUB5mYM+roqnUZg= github.com/aquasecurity/tracee/api v0.0.0-20240905132323-d1eaeef6a19f/go.mod h1:Gn6xVkaBkVe1pOQ0++uuHl+lMMClv0TPY8mCQ6j88aA= github.com/aquasecurity/tracee/signatures/helpers v0.0.0-20240607205742-90c301111aee h1:1KJy6Z2bSpmKQVPShU7hhbXgGVOgMwvzf9rjoWMTYEg= diff --git a/pkg/ebpf/c/common/common.h b/pkg/ebpf/c/common/common.h index f35146d0e01d..3f3b4eb23b8c 100644 --- a/pkg/ebpf/c/common/common.h +++ b/pkg/ebpf/c/common/common.h @@ -62,7 +62,7 @@ static __inline int has_prefix(char *prefix, char *str, int n) } // prefix is too long - return 0; + return 1; } #endif diff --git a/pkg/ebpf/c/common/memory.h b/pkg/ebpf/c/common/memory.h index d8a7aca2375b..491617f2a5fe 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,17 @@ 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; + + // read only 6 characters (7 with NULL terminator), enough to compare with "[vdso]" + char mapping_name[7]; + bpf_probe_read_str(&mapping_name, 7, BPF_CORE_READ(special_mapping, name)); + return has_prefix("[vdso]", mapping_name, 6); +} + #endif diff --git a/pkg/ebpf/c/tracee.bpf.c b/pkg/ebpf/c/tracee.bpf.c index 4a7cc6089104..551f2b688237 100644 --- a/pkg/ebpf/c/tracee.bpf.c +++ b/pkg/ebpf/c/tracee.bpf.c @@ -5182,8 +5182,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 61396bb5aa59..090ff861ac2b 100644 --- a/pkg/ebpf/c/vmlinux.h +++ b/pkg/ebpf/c/vmlinux.h @@ -291,6 +291,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 { @@ -301,7 +311,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/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 {