diff --git a/pkg/ebpf/c/maps.h b/pkg/ebpf/c/maps.h index fe415e403484..54073e7cc5b4 100644 --- a/pkg/ebpf/c/maps.h +++ b/pkg/ebpf/c/maps.h @@ -22,6 +22,10 @@ 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_1, + TAIL_HIDDEN_KERNEL_MODULE_MODTREE_2, + TAIL_HIDDEN_KERNEL_MODULE_MODTREE_3, + TAIL_HIDDEN_KERNEL_MODULE_MODTREE_4, MAX_TAIL_CALL }; diff --git a/pkg/ebpf/c/tracee.bpf.c b/pkg/ebpf/c/tracee.bpf.c index 6219e46d3ede..ce3d4e5604a5 100644 --- a/pkg/ebpf/c/tracee.bpf.c +++ b/pkg/ebpf/c/tracee.bpf.c @@ -747,7 +747,8 @@ enum struct modules_map { __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, MAX_NUM_MODULES); + __uint(max_entries, 2 * MAX_NUM_MODULES); // Do not decrease as it won't be big enough to hold + // all loaded modules __type(key, u64); __type(value, kernel_module_t); } modules_map SEC(".maps"); @@ -763,6 +764,15 @@ struct new_module_map { typedef struct new_module_map new_module_map_t; +struct module_context_map { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 2); + __type(key, u32); + __type(value, u64); +} 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 @@ -841,7 +851,6 @@ statfunc int init_shown_modules() if (&pos->list == head) { return 0; } - bpf_map_update_elem(&modules_map, &pos, &ker_mod, BPF_ANY); } @@ -933,11 +942,15 @@ 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; +}; + +statfunc int mod_tree_loop(program_data_t *p, struct rb_node *addr, int idx) { struct latch_tree_node *ltn; struct module *mod; - struct rb_node *curr = root; + struct rb_node *curr = addr; u32 flags = MOD_TREE; #pragma unroll @@ -970,12 +983,145 @@ statfunc int walk_mod_tree(program_data_t *p, struct rb_node *root, int idx) } } + int key = 0; + bpf_map_update_elem( + &module_context_map, &key, &curr, BPF_ANY); // To be able to continue in the tailcall return HID_MOD_UNCOMPLETED_ITERATIONS; } -struct mod_tree_root { - struct latch_tree_root root; -}; +SEC("uprobe/lkm_seeker_modtree_1_tail") +int lkm_seeker_modtree_1_tail(struct pt_regs *ctx) +{ + program_data_t p = {}; + if (!init_tailcall_program_data(&p, ctx)) + return -1; + + int key = 0; + u64 *addr = (u64 *) bpf_map_lookup_elem(&module_context_map, &key); + if (addr == NULL) + return -1; + + struct rb_node *curr = (struct rb_node *) *addr; + int k = 1; + int *idx = (int *) bpf_map_lookup_elem(&module_context_map, &k); + if (idx == NULL) + return -1; + + u32 flags = HISTORY_SCAN_FINISHED; + int ret = mod_tree_loop(&p, curr, *idx); + if (ret == 0) { + lkm_seeker_send_to_userspace((struct module *) HISTORY_SCAN_SUCCESSFUL, &flags, &p); + } else if (ret == HID_MOD_UNCOMPLETED_ITERATIONS) { // Continue doing more iterations + bpf_tail_call(ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_MODTREE_2); + } else { + 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); + } + + bpf_tail_call(ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_PROC); + + return 0; +} + +SEC("uprobe/lkm_seeker_modtree_2_tail") +int lkm_seeker_modtree_2_tail(struct pt_regs *ctx) +{ + program_data_t p = {}; + if (!init_tailcall_program_data(&p, ctx)) + return -1; + + int key = 0; + u64 *addr = (u64 *) bpf_map_lookup_elem(&module_context_map, &key); + if (addr == NULL) + return -1; + + struct rb_node *curr = (struct rb_node *) *addr; + int k = 1; + int *idx = (int *) bpf_map_lookup_elem(&module_context_map, &k); + if (idx == NULL) + return -1; + + u32 flags = HISTORY_SCAN_FINISHED; + int ret = mod_tree_loop(&p, curr, *idx); + if (ret == 0) { + lkm_seeker_send_to_userspace((struct module *) HISTORY_SCAN_SUCCESSFUL, &flags, &p); + } else if (ret == HID_MOD_UNCOMPLETED_ITERATIONS) { // Continue doing more iterations + bpf_tail_call(ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_MODTREE_3); + } else { + 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); + } + + bpf_tail_call(ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_PROC); + + return -1; +} + +SEC("uprobe/lkm_seeker_modtree_3_tail") +int lkm_seeker_modtree_3_tail(struct pt_regs *ctx) +{ + program_data_t p = {}; + if (!init_tailcall_program_data(&p, ctx)) + return -1; + + int key = 0; + u64 *addr = (u64 *) bpf_map_lookup_elem(&module_context_map, &key); + if (addr == NULL) + return -1; + + struct rb_node *curr = (struct rb_node *) *addr; + int k = 1; + int *idx = (int *) bpf_map_lookup_elem(&module_context_map, &k); + if (idx == NULL) + return -1; + + u32 flags = HISTORY_SCAN_FINISHED; + int ret = mod_tree_loop(&p, curr, *idx); + if (ret == 0) { + lkm_seeker_send_to_userspace((struct module *) HISTORY_SCAN_SUCCESSFUL, &flags, &p); + } else if (ret == HID_MOD_UNCOMPLETED_ITERATIONS) { // Continue doing more iterations + bpf_tail_call(ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_MODTREE_4); + } else { + 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); + } + + bpf_tail_call(ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_PROC); + + return -1; +} + +SEC("uprobe/lkm_seeker_modtree_4_tail") +int lkm_seeker_modtree_4_tail(struct pt_regs *ctx) +{ + program_data_t p = {}; + if (!init_tailcall_program_data(&p, ctx)) + return -1; + + int key = 0; + u64 *addr = (u64 *) bpf_map_lookup_elem(&module_context_map, &key); + if (addr == NULL) + return -1; + + struct rb_node *curr = (struct rb_node *) *addr; + int k = 1; + int *idx = (int *) bpf_map_lookup_elem(&module_context_map, &k); + if (idx == NULL) + return -1; + + u32 flags = HISTORY_SCAN_FINISHED; + int ret = mod_tree_loop(&p, curr, *idx); + if (ret == 0) { + lkm_seeker_send_to_userspace((struct module *) HISTORY_SCAN_SUCCESSFUL, &flags, &p); + } else { + 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); + } + + bpf_tail_call(ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_PROC); + + return -1; +} statfunc int find_modules_from_mod_tree(program_data_t *p) { @@ -989,9 +1135,12 @@ 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 key = 1; + int idx = seq & 1; + bpf_map_update_elem(&module_context_map, &key, &idx, BPF_ANY); - return walk_mod_tree(p, node, seq & 1); + struct rb_node *node = BPF_CORE_READ(m_tree, root.tree[idx].rb_node); + return mod_tree_loop(p, node, idx); } static __always_inline u64 check_new_mods_only(program_data_t *p) @@ -1222,18 +1371,16 @@ 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) { + if (ret == 0) { + lkm_seeker_send_to_userspace((struct module *) HISTORY_SCAN_SUCCESSFUL, &flags, &p); + } else if (ret == HID_MOD_UNCOMPLETED_ITERATIONS) { // Continue doing more iterations + bpf_tail_call(ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_MODTREE_1); + } else { 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; + lkm_seeker_send_to_userspace((struct module *) HISTORY_SCAN_FAILURE, &flags, &p); } - // Report to userspace that the history scan finished successfully - lkm_seeker_send_to_userspace((struct module *) HISTORY_SCAN_SUCCESSFUL, &flags, &p); - bpf_tail_call(ctx, &prog_array, TAIL_HIDDEN_KERNEL_MODULE_PROC); - return -1; } @@ -2500,14 +2647,14 @@ int BPF_KPROBE(trace_debugfs_create_file) return 0; char *name = (char *) PT_REGS_PARM1(ctx); - umode_t mode = (unsigned short) PT_REGS_PARM2(ctx); + mode_t mode = (unsigned short) PT_REGS_PARM2(ctx); struct dentry *dentry = (struct dentry *) PT_REGS_PARM3(ctx); void *dentry_path = get_dentry_path_str(dentry); unsigned long proc_ops_addr = (unsigned long) PT_REGS_PARM5(ctx); save_str_to_buf(&p.event->args_buf, name, 0); save_str_to_buf(&p.event->args_buf, dentry_path, 1); - save_to_submit_buf(&p.event->args_buf, &mode, sizeof(umode_t), 2); + save_to_submit_buf(&p.event->args_buf, &mode, sizeof(mode_t), 2); save_to_submit_buf(&p.event->args_buf, (void *) &proc_ops_addr, sizeof(u64), 3); return events_perf_submit(&p, 0); 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 b0adc1f25e5d..0e1e0e6ec187 100644 --- a/pkg/events/core.go +++ b/pkg/events/core.go @@ -12132,6 +12132,10 @@ 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_1_tail", []uint32{TailHiddenKernelModuleModTree1}}, + {"prog_array", "lkm_seeker_modtree_2_tail", []uint32{TailHiddenKernelModuleModTree2}}, + {"prog_array", "lkm_seeker_modtree_3_tail", []uint32{TailHiddenKernelModuleModTree3}}, + {"prog_array", "lkm_seeker_modtree_4_tail", []uint32{TailHiddenKernelModuleModTree4}}, }, }, sets: []string{}, diff --git a/pkg/events/definition_dependencies.go b/pkg/events/definition_dependencies.go index 31f651df8711..5117b1bad7ad 100644 --- a/pkg/events/definition_dependencies.go +++ b/pkg/events/definition_dependencies.go @@ -152,6 +152,10 @@ const ( TailHiddenKernelModuleKset TailHiddenKernelModuleModTree TailHiddenKernelModuleNewModOnly + TailHiddenKernelModuleModTree1 + TailHiddenKernelModuleModTree2 + TailHiddenKernelModuleModTree3 + TailHiddenKernelModuleModTree4 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