Skip to content

Commit

Permalink
tracing/kprobes: Add symbol counting check when module loads
Browse files Browse the repository at this point in the history
Currently, kprobe event checks whether the target symbol name is unique
or not, so that it does not put a probe on an unexpected place. But this
skips the check if the target is on a module because the module may not
be loaded.

To fix this issue, this patch checks the number of probe target symbols
in a target module when the module is loaded. If the probe is not on the
unique name symbols in the module, it will be rejected at that point.

Note that the symbol which has a unique name in the target module,
it will be accepted even if there are same-name symbols in the
kernel or other modules,

Link: https://lore.kernel.org/all/172016348553.99543.2834679315611882137.stgit@devnote2/

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Reviewed-by: Steven Rostedt (Google) <rostedt@goodmis.org>
  • Loading branch information
Masami Hiramatsu (Google) committed Jul 6, 2024
1 parent 637c26f commit 9d86160
Showing 1 changed file with 81 additions and 44 deletions.
125 changes: 81 additions & 44 deletions kernel/trace/trace_kprobe.c
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,21 @@ static int register_trace_kprobe(struct trace_kprobe *tk)
}

#ifdef CONFIG_MODULES
static int validate_module_probe_symbol(const char *modname, const char *symbol);

static int register_module_trace_kprobe(struct module *mod, struct trace_kprobe *tk)
{
const char *p;
int ret = 0;

p = strchr(trace_kprobe_symbol(tk), ':');
if (p)
ret = validate_module_probe_symbol(module_name(mod), p + 1);
if (!ret)
ret = __register_trace_kprobe(tk);
return ret;
}

/* Module notifier call back, checking event on the module */
static int trace_kprobe_module_callback(struct notifier_block *nb,
unsigned long val, void *data)
Expand All @@ -696,7 +711,7 @@ static int trace_kprobe_module_callback(struct notifier_block *nb,
if (trace_kprobe_within_module(tk, mod)) {
/* Don't need to check busy - this should have gone. */
__unregister_trace_kprobe(tk);
ret = __register_trace_kprobe(tk);
ret = register_module_trace_kprobe(mod, tk);
if (ret)
pr_warn("Failed to re-register probe %s on %s: %d\n",
trace_probe_name(&tk->tp),
Expand Down Expand Up @@ -747,17 +762,68 @@ static int count_mod_symbols(void *data, const char *name, unsigned long unused)
return 0;
}

static unsigned int number_of_same_symbols(char *func_name)
static unsigned int number_of_same_symbols(const char *mod, const char *func_name)
{
struct sym_count_ctx ctx = { .count = 0, .name = func_name };

kallsyms_on_each_match_symbol(count_symbols, func_name, &ctx.count);
if (!mod)
kallsyms_on_each_match_symbol(count_symbols, func_name, &ctx.count);

module_kallsyms_on_each_symbol(NULL, count_mod_symbols, &ctx);
module_kallsyms_on_each_symbol(mod, count_mod_symbols, &ctx);

return ctx.count;
}

static int validate_module_probe_symbol(const char *modname, const char *symbol)
{
unsigned int count = number_of_same_symbols(modname, symbol);

if (count > 1) {
/*
* Users should use ADDR to remove the ambiguity of
* using KSYM only.
*/
return -EADDRNOTAVAIL;
} else if (count == 0) {
/*
* We can return ENOENT earlier than when register the
* kprobe.
*/
return -ENOENT;
}
return 0;
}

static int validate_probe_symbol(char *symbol)
{
struct module *mod = NULL;
char *modname = NULL, *p;
int ret = 0;

p = strchr(symbol, ':');
if (p) {
modname = symbol;
symbol = p + 1;
*p = '\0';
/* Return 0 (defer) if the module does not exist yet. */
rcu_read_lock_sched();
mod = find_module(modname);
if (mod && !try_module_get(mod))
mod = NULL;
rcu_read_unlock_sched();
if (!mod)
goto out;
}

ret = validate_module_probe_symbol(modname, symbol);
out:
if (p)
*p = ':';
if (mod)
module_put(mod);
return ret;
}

static int trace_kprobe_entry_handler(struct kretprobe_instance *ri,
struct pt_regs *regs);

Expand Down Expand Up @@ -881,6 +947,14 @@ static int __trace_kprobe_create(int argc, const char *argv[])
trace_probe_log_err(0, BAD_PROBE_ADDR);
goto parse_error;
}
ret = validate_probe_symbol(symbol);
if (ret) {
if (ret == -EADDRNOTAVAIL)
trace_probe_log_err(0, NON_UNIQ_SYMBOL);
else
trace_probe_log_err(0, BAD_PROBE_ADDR);
goto parse_error;
}
if (is_return)
ctx.flags |= TPARG_FL_RETURN;
ret = kprobe_on_func_entry(NULL, symbol, offset);
Expand All @@ -893,31 +967,6 @@ static int __trace_kprobe_create(int argc, const char *argv[])
}
}

if (symbol && !strchr(symbol, ':')) {
unsigned int count;

count = number_of_same_symbols(symbol);
if (count > 1) {
/*
* Users should use ADDR to remove the ambiguity of
* using KSYM only.
*/
trace_probe_log_err(0, NON_UNIQ_SYMBOL);
ret = -EADDRNOTAVAIL;

goto error;
} else if (count == 0) {
/*
* We can return ENOENT earlier than when register the
* kprobe.
*/
trace_probe_log_err(0, BAD_PROBE_ADDR);
ret = -ENOENT;

goto error;
}
}

trace_probe_log_set_index(0);
if (event) {
ret = traceprobe_parse_event_name(&event, &group, gbuf,
Expand Down Expand Up @@ -1835,21 +1884,9 @@ create_local_trace_kprobe(char *func, void *addr, unsigned long offs,
char *event;

if (func) {
unsigned int count;

count = number_of_same_symbols(func);
if (count > 1)
/*
* Users should use addr to remove the ambiguity of
* using func only.
*/
return ERR_PTR(-EADDRNOTAVAIL);
else if (count == 0)
/*
* We can return ENOENT earlier than when register the
* kprobe.
*/
return ERR_PTR(-ENOENT);
ret = validate_probe_symbol(func);
if (ret)
return ERR_PTR(ret);
}

/*
Expand Down

0 comments on commit 9d86160

Please sign in to comment.