From c8cb4a7d99f82d35cabb6689f951a30bfb0e1bc5 Mon Sep 17 00:00:00 2001 From: Ori Glassman Date: Tue, 10 Dec 2024 12:34:02 +0200 Subject: [PATCH] fix(events): fix hidden_kernel_module history scan for kernels >6.2 - also address potential slice out of bounds for name argument in hidden_kernel_module.go --- pkg/ebpf/c/maps.h | 1 + pkg/ebpf/c/tracee.bpf.c | 110 ++++++++++++++++------ pkg/ebpf/c/tracee.h | 1 - pkg/events/core.go | 1 + pkg/events/definition_dependencies.go | 1 + pkg/events/derive/hidden_kernel_module.go | 8 +- 6 files changed, 88 insertions(+), 34 deletions(-) diff --git a/pkg/ebpf/c/maps.h b/pkg/ebpf/c/maps.h index 17f36dae37bd..d336fbc18499 100644 --- a/pkg/ebpf/c/maps.h +++ b/pkg/ebpf/c/maps.h @@ -22,6 +22,7 @@ enum tail_call_id_e TAIL_HIDDEN_KERNEL_MODULE_KSET, TAIL_HIDDEN_KERNEL_MODULE_MOD_TREE, TAIL_HIDDEN_KERNEL_MODULE_NEW_MOD_ONLY, + TAIL_HIDDEN_KERNEL_MODULE_MODTREE_LOOP, MAX_TAIL_CALL }; diff --git a/pkg/ebpf/c/tracee.bpf.c b/pkg/ebpf/c/tracee.bpf.c index bf2584029954..d9b6be7463df 100644 --- a/pkg/ebpf/c/tracee.bpf.c +++ b/pkg/ebpf/c/tracee.bpf.c @@ -726,10 +726,12 @@ int tracepoint__sched__sched_process_fork(struct bpf_raw_tracepoint_args *ctx) return 0; } -// number of iterations - value that the verifier was seen to cope with - the higher, the better -#define MAX_NUM_MODULES 440 -#define HISTORY_SCAN_FAILURE 0 -#define HISTORY_SCAN_SUCCESSFUL 1 +#define MAX_NUM_MODULES 440 +#define MAX_MODULES_MAP_ENTRIES 2 * MAX_NUM_MODULES +#define MOD_TREE_LOOP_ITERATIONS 240 +#define MOD_TREE_LOOP_DEPTH 14 +#define HISTORY_SCAN_FAILURE 0 +#define HISTORY_SCAN_SUCCESSFUL 1 enum { @@ -747,7 +749,7 @@ enum struct modules_map { __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, MAX_NUM_MODULES); + __uint(max_entries, MAX_MODULES_MAP_ENTRIES); __type(key, u64); __type(value, kernel_module_t); } modules_map SEC(".maps"); @@ -763,6 +765,21 @@ struct new_module_map { typedef struct new_module_map new_module_map_t; +typedef struct module_context_args { + struct rb_node *curr; + int iteration_num; + int idx; +} module_context_args_t; + +struct module_context_map { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 1); + __type(key, u32); + __type(value, module_context_args_t); +} module_context_map SEC(".maps"); + +typedef struct module_context_map module_context_map_t; + // We only care for modules that got deleted or inserted between our scan and if // we detected something suspicious. Since it's a very small time frame, it's // not likely that a large amount of modules will be deleted. Instead of saving @@ -786,6 +803,7 @@ u64 last_module_insert_time = 0; bool hidden_old_mod_scan_done = false; static const int HID_MOD_RACE_CONDITION = -1; static const int HID_MOD_UNCOMPLETED_ITERATIONS = -2; +static const int HID_MOD_COMPLETED_ITERATIONS = 0; static const int HID_MOD_MEM_ZEROED = -3; static const int MOD_HIDDEN = 1; static const int MOD_NOT_HIDDEN = 0; @@ -841,7 +859,6 @@ statfunc int init_shown_modules() if (&pos->list == head) { return 0; } - bpf_map_update_elem(&modules_map, &pos, &ker_mod, BPF_ANY); } @@ -933,15 +950,34 @@ statfunc struct latch_tree_node *__lt_from_rb(struct rb_node *node, int idx) return container_of(node, struct latch_tree_node, node[idx]); } -statfunc int walk_mod_tree(program_data_t *p, struct rb_node *root, int idx) +struct mod_tree_root { + struct latch_tree_root root; +}; + +SEC("uprobe/lkm_seeker_modtree_loop_tail") +int lkm_seeker_modtree_loop(struct pt_regs *ctx) { + program_data_t p = {}; + if (!init_tailcall_program_data(&p, ctx)) + return -1; + struct latch_tree_node *ltn; struct module *mod; - struct rb_node *curr = root; u32 flags = MOD_TREE; + int key = 0; + module_context_args_t *module_ctx_args = bpf_map_lookup_elem(&module_context_map, &key); + if (module_ctx_args == NULL) + return -1; + + struct rb_node *curr = module_ctx_args->curr; + int idx = module_ctx_args->idx; + int iteration_num = module_ctx_args->iteration_num; + + int loop_result = HID_MOD_UNCOMPLETED_ITERATIONS; + #pragma unroll - for (int i = 0; i < MAX_NUM_MODULES; i++) { + for (int i = 0; i < MOD_TREE_LOOP_ITERATIONS; i++) { if (curr != NULL) { rb_node_t rb_nod = {.node = curr}; bpf_map_push_elem(&walk_mod_tree_queue, &rb_nod, BPF_EXIST); @@ -950,7 +986,8 @@ statfunc int walk_mod_tree(program_data_t *p, struct rb_node *root, int idx) } else { rb_node_t rb_nod; if (bpf_map_pop_elem(&walk_mod_tree_queue, &rb_nod) != 0) { - return 0; // Finished iterating + loop_result = HID_MOD_COMPLETED_ITERATIONS; + break; } else { curr = rb_nod.node; ltn = __lt_from_rb(curr, idx); @@ -958,9 +995,10 @@ statfunc int walk_mod_tree(program_data_t *p, struct rb_node *root, int idx) int ret = is_hidden((u64) mod); if (ret == MOD_HIDDEN) { - lkm_seeker_send_to_userspace(mod, &flags, p); + lkm_seeker_send_to_userspace(mod, &flags, &p); } else if (ret == HID_MOD_RACE_CONDITION) { - return ret; + loop_result = HID_MOD_RACE_CONDITION; + break; } /* We have visited the node and its left subtree. @@ -970,12 +1008,27 @@ statfunc int walk_mod_tree(program_data_t *p, struct rb_node *root, int idx) } } - return HID_MOD_UNCOMPLETED_ITERATIONS; -} + iteration_num++; -struct mod_tree_root { - struct latch_tree_root root; -}; + if (loop_result == HID_MOD_COMPLETED_ITERATIONS) { + flags = HISTORY_SCAN_FINISHED; + lkm_seeker_send_to_userspace((struct module *) HISTORY_SCAN_SUCCESSFUL, &flags, &p); + bpf_tail_call(ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_PROC); + } else if (loop_result == HID_MOD_RACE_CONDITION || iteration_num == MOD_TREE_LOOP_DEPTH) { + flags = HISTORY_SCAN_FINISHED; + tracee_log(ctx, BPF_LOG_LVL_WARN, BPF_LOG_ID_HID_KER_MOD, loop_result ^ iteration_num); + lkm_seeker_send_to_userspace((struct module *) HISTORY_SCAN_FAILURE, &flags, &p); + bpf_tail_call(ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_PROC); + } + + // Update context args for the next recursive call + module_ctx_args->iteration_num = iteration_num; + module_ctx_args->curr = curr; + + bpf_tail_call(ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_MODTREE_LOOP); + + return -1; +} statfunc int find_modules_from_mod_tree(program_data_t *p) { @@ -989,9 +1042,16 @@ statfunc int find_modules_from_mod_tree(program_data_t *p) seq = BPF_CORE_READ(m_tree, root.seq.seqcount.sequence); // version >= v5.10 } - struct rb_node *node = BPF_CORE_READ(m_tree, root.tree[seq & 1].rb_node); + int idx = seq & 1; + struct rb_node *root = BPF_CORE_READ(m_tree, root.tree[idx].rb_node); + module_context_args_t module_ctx_args = {.idx = idx, .iteration_num = 0, .curr = root}; + + int key = 0; + bpf_map_update_elem(&module_context_map, &key, &module_ctx_args, BPF_ANY); - return walk_mod_tree(p, node, seq & 1); + bpf_tail_call(p->ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_MODTREE_LOOP); + + return -1; } static __always_inline u64 check_new_mods_only(program_data_t *p) @@ -1221,19 +1281,9 @@ int lkm_seeker_mod_tree_tail(struct pt_regs *ctx) // This method is efficient only when the kernel is compiled with // CONFIG_MODULES_TREE_LOOKUP=y - int ret = find_modules_from_mod_tree(&p); - if (ret < 0) { - tracee_log(ctx, BPF_LOG_LVL_WARN, BPF_LOG_ID_HID_KER_MOD, ret); - lkm_seeker_send_to_userspace( - (struct module *) HISTORY_SCAN_FAILURE, &flags, &p); // Report failure of history scan - return -1; - } - - // Report to userspace that the history scan finished successfully - lkm_seeker_send_to_userspace((struct module *) HISTORY_SCAN_SUCCESSFUL, &flags, &p); + find_modules_from_mod_tree(&p); bpf_tail_call(ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_PROC); - return -1; } diff --git a/pkg/ebpf/c/tracee.h b/pkg/ebpf/c/tracee.h index 0465b7a037fa..53d78240a2d7 100644 --- a/pkg/ebpf/c/tracee.h +++ b/pkg/ebpf/c/tracee.h @@ -35,7 +35,6 @@ statfunc int init_shown_modules(); statfunc int is_hidden(u64); statfunc int find_modules_from_module_kset_list(program_data_t *); statfunc struct latch_tree_node *__lt_from_rb(struct rb_node *, int); -statfunc int walk_mod_tree(program_data_t *p, struct rb_node *, int); statfunc int find_modules_from_mod_tree(program_data_t *); statfunc int check_is_proc_modules_hooked(program_data_t *); diff --git a/pkg/events/core.go b/pkg/events/core.go index 0aca7e0628e2..146b58bd63ba 100644 --- a/pkg/events/core.go +++ b/pkg/events/core.go @@ -12133,6 +12133,7 @@ var CoreEvents = map[ID]Definition{ {"prog_array", "lkm_seeker_kset_tail", []uint32{TailHiddenKernelModuleKset}}, {"prog_array", "lkm_seeker_mod_tree_tail", []uint32{TailHiddenKernelModuleModTree}}, {"prog_array", "lkm_seeker_new_mod_only_tail", []uint32{TailHiddenKernelModuleNewModOnly}}, + {"prog_array", "lkm_seeker_modtree_loop", []uint32{TailHiddenKernelModuleModTreeLoop}}, }, }, sets: []string{}, diff --git a/pkg/events/definition_dependencies.go b/pkg/events/definition_dependencies.go index 31f651df8711..ae898fcf73a8 100644 --- a/pkg/events/definition_dependencies.go +++ b/pkg/events/definition_dependencies.go @@ -152,6 +152,7 @@ const ( TailHiddenKernelModuleKset TailHiddenKernelModuleModTree TailHiddenKernelModuleNewModOnly + TailHiddenKernelModuleModTreeLoop MaxTail ) diff --git a/pkg/events/derive/hidden_kernel_module.go b/pkg/events/derive/hidden_kernel_module.go index 489c635d8408..8b3f55e2ad58 100644 --- a/pkg/events/derive/hidden_kernel_module.go +++ b/pkg/events/derive/hidden_kernel_module.go @@ -165,15 +165,17 @@ func handleHistoryScanFinished(scanStatus uint64) ([][]interface{}, []error) { // extractFromEvent extract arguments from the trace.Argument func extractFromEvent(args []trace.Argument, address uint64) []interface{} { // Parse module name if possible - var name string + name := "" nameBytes, err := parse.ArgVal[[]byte](args, "name") if err != nil { - name = "" // Don't fail hard, submit it without a name! logger.Debugw("Failed extracting hidden module name") } else { // Remove the trailing terminating characters. - name = string(nameBytes[:bytes.IndexByte(nameBytes[:], 0)]) + index := bytes.IndexByte(nameBytes[:], 0) + if index > 0 { + name = string(nameBytes[:index]) + } } // Parse module srcversion if possible