Skip to content

Commit

Permalink
fix(events): fix hidden_kernel_module history scan for kernels >6.2
Browse files Browse the repository at this point in the history
- also address potential slice out of bounds for name argument in
  hidden_kernel_module.go
  • Loading branch information
OriGlassman authored and randomname21 committed Dec 10, 2024
1 parent 9b8d817 commit c8cb4a7
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 34 deletions.
1 change: 1 addition & 0 deletions pkg/ebpf/c/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

Expand Down
110 changes: 80 additions & 30 deletions pkg/ebpf/c/tracee.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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");
Expand All @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
Expand All @@ -950,17 +986,19 @@ 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);
mod = BPF_CORE_READ(container_of(ltn, struct mod_tree_node, node), mod);

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.
Expand All @@ -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)
{
Expand All @@ -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)
Expand Down Expand Up @@ -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;
}

Expand Down
1 change: 0 additions & 1 deletion pkg/ebpf/c/tracee.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);

Expand Down
1 change: 1 addition & 0 deletions pkg/events/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{},
Expand Down
1 change: 1 addition & 0 deletions pkg/events/definition_dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ const (
TailHiddenKernelModuleKset
TailHiddenKernelModuleModTree
TailHiddenKernelModuleNewModOnly
TailHiddenKernelModuleModTreeLoop
MaxTail
)

Expand Down
8 changes: 5 additions & 3 deletions pkg/events/derive/hidden_kernel_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit c8cb4a7

Please sign in to comment.