Skip to content

Commit

Permalink
kprobes: Fix possible use-after-free issue on kprobe registration
Browse files Browse the repository at this point in the history
When unloading a module, its state is changing MODULE_STATE_LIVE ->
 MODULE_STATE_GOING -> MODULE_STATE_UNFORMED. Each change will take
a time. `is_module_text_address()` and `__module_text_address()`
works with MODULE_STATE_LIVE and MODULE_STATE_GOING.
If we use `is_module_text_address()` and `__module_text_address()`
separately, there is a chance that the first one is succeeded but the
next one is failed because module->state becomes MODULE_STATE_UNFORMED
between those operations.

In `check_kprobe_address_safe()`, if the second `__module_text_address()`
is failed, that is ignored because it expected a kernel_text address.
But it may have failed simply because module->state has been changed
to MODULE_STATE_UNFORMED. In this case, arm_kprobe() will try to modify
non-exist module text address (use-after-free).

To fix this problem, we should not use separated `is_module_text_address()`
and `__module_text_address()`, but use only `__module_text_address()`
once and do `try_module_get(module)` which is only available with
MODULE_STATE_LIVE.

Link: https://lore.kernel.org/all/[email protected]/

Fixes: 28f6c37 ("kprobes: Forbid probing on trampoline and BPF code areas")
Cc: [email protected]
Signed-off-by: Zheng Yejian <[email protected]>
Signed-off-by: Masami Hiramatsu (Google) <[email protected]>
  • Loading branch information
Zheng Yejian authored and mhiramat committed Apr 10, 2024
1 parent fec50db commit 325f3fb
Showing 1 changed file with 12 additions and 6 deletions.
18 changes: 12 additions & 6 deletions kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1567,10 +1567,17 @@ static int check_kprobe_address_safe(struct kprobe *p,
jump_label_lock();
preempt_disable();

/* Ensure it is not in reserved area nor out of text */
if (!(core_kernel_text((unsigned long) p->addr) ||
is_module_text_address((unsigned long) p->addr)) ||
in_gate_area_no_mm((unsigned long) p->addr) ||
/* Ensure the address is in a text area, and find a module if exists. */
*probed_mod = NULL;
if (!core_kernel_text((unsigned long) p->addr)) {
*probed_mod = __module_text_address((unsigned long) p->addr);
if (!(*probed_mod)) {
ret = -EINVAL;
goto out;
}
}
/* Ensure it is not in reserved area. */
if (in_gate_area_no_mm((unsigned long) p->addr) ||
within_kprobe_blacklist((unsigned long) p->addr) ||
jump_label_text_reserved(p->addr, p->addr) ||
static_call_text_reserved(p->addr, p->addr) ||
Expand All @@ -1580,8 +1587,7 @@ static int check_kprobe_address_safe(struct kprobe *p,
goto out;
}

/* Check if 'p' is probing a module. */
*probed_mod = __module_text_address((unsigned long) p->addr);
/* Get module refcount and reject __init functions for loaded modules. */
if (*probed_mod) {
/*
* We must hold a refcount of the probed module while updating
Expand Down

0 comments on commit 325f3fb

Please sign in to comment.