Merge tag 'probes-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull probes updates from Masami Hiramatsu:
 "Uprobes:

   - x86/shstk: Make return uprobe work with shadow stack

   - Add uretprobe syscall which speeds up the uretprobe 10-30% faster.
     This syscall is automatically used from user-space trampolines
     which are generated by the uretprobe. If this syscall is used by
     normal user program, it will cause SIGILL. Note that this is
     currently only implemented on x86_64.

     (This also has two fixes for adjusting the syscall number to avoid
     conflict with new *attrat syscalls.)

   - uprobes/perf: fix user stack traces in the presence of pending
     uretprobe. This corrects the uretprobe's trampoline address in the
     stacktrace with correct return address

   - selftests/x86: Add a return uprobe with shadow stack test

   - selftests/bpf: Add uretprobe syscall related tests.
      - test case for register integrity check
      - test case with register changing case
      - test case for uretprobe syscall without uprobes (expected to fail)
      - test case for uretprobe with shadow stack

   - selftests/bpf: add test validating uprobe/uretprobe stack traces

   - MAINTAINERS: Add uprobes entry. This does not specify the tree but
     to clarify who maintains and reviews the uprobes

  Kprobes:

   - tracing/kprobes: Test case cleanups.

     Replace redundant WARN_ON_ONCE() + pr_warn() with WARN_ONCE() and
     remove unnecessary code from selftest

   - tracing/kprobes: Add symbol counting check when module loads.

     This checks the uniqueness of the probed symbol on modules. The
     same check has already done for kernel symbols

     (This also has a fix for build error with CONFIG_MODULES=n)

  Cleanup:

   - Add MODULE_DESCRIPTION() macros for fprobe and kprobe examples"

* tag 'probes-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  MAINTAINERS: Add uprobes entry
  selftests/bpf: Change uretprobe syscall number in uprobe_syscall test
  uprobe: Change uretprobe syscall scope and number
  tracing/kprobes: Fix build error when find_module() is not available
  tracing/kprobes: Add symbol counting check when module loads
  selftests/bpf: add test validating uprobe/uretprobe stack traces
  perf,uprobes: fix user stack traces in the presence of pending uretprobes
  tracing/kprobe: Remove cleanup code unrelated to selftest
  tracing/kprobe: Integrate test warnings into WARN_ONCE
  selftests/bpf: Add uretprobe shadow stack test
  selftests/bpf: Add uretprobe syscall call from user space test
  selftests/bpf: Add uretprobe syscall test for regs changes
  selftests/bpf: Add uretprobe syscall test for regs integrity
  selftests/x86: Add return uprobe shadow stack test
  uprobe: Add uretprobe syscall to speed up return probe
  uprobe: Wire up uretprobe system call
  x86/shstk: Make return uprobe work with shadow stack
  samples: kprobes: add missing MODULE_DESCRIPTION() macros
  fprobe: add missing MODULE_DESCRIPTION() macro
This commit is contained in:
Linus Torvalds
2024-07-18 12:19:20 -07:00
23 changed files with 1320 additions and 92 deletions
+111 -81
View File
@@ -678,6 +678,21 @@ end:
}
#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)
@@ -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),
@@ -747,17 +762,81 @@ 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;
}
#ifdef CONFIG_MODULES
/* Return NULL if the module is not loaded or under unloading. */
static struct module *try_module_get_by_name(const char *name)
{
struct module *mod;
rcu_read_lock_sched();
mod = find_module(name);
if (mod && !try_module_get(mod))
mod = NULL;
rcu_read_unlock_sched();
return mod;
}
#else
#define try_module_get_by_name(name) (NULL)
#endif
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';
mod = try_module_get_by_name(modname);
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);
@@ -881,6 +960,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);
@@ -893,31 +980,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,
@@ -1835,21 +1897,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);
}
/*
@@ -2023,19 +2073,16 @@ static __init int kprobe_trace_self_tests_init(void)
pr_info("Testing kprobe tracing: ");
ret = create_or_delete_trace_kprobe("p:testprobe kprobe_trace_selftest_target $stack $stack0 +0($stack)");
if (WARN_ON_ONCE(ret)) {
pr_warn("error on probing function entry.\n");
if (WARN_ONCE(ret, "error on probing function entry.")) {
warn++;
} else {
/* Enable trace point */
tk = find_trace_kprobe("testprobe", KPROBE_EVENT_SYSTEM);
if (WARN_ON_ONCE(tk == NULL)) {
pr_warn("error on getting new probe.\n");
if (WARN_ONCE(tk == NULL, "error on probing function entry.")) {
warn++;
} else {
file = find_trace_probe_file(tk, top_trace_array());
if (WARN_ON_ONCE(file == NULL)) {
pr_warn("error on getting probe file.\n");
if (WARN_ONCE(file == NULL, "error on getting probe file.")) {
warn++;
} else
enable_trace_kprobe(
@@ -2044,19 +2091,16 @@ static __init int kprobe_trace_self_tests_init(void)
}
ret = create_or_delete_trace_kprobe("r:testprobe2 kprobe_trace_selftest_target $retval");
if (WARN_ON_ONCE(ret)) {
pr_warn("error on probing function return.\n");
if (WARN_ONCE(ret, "error on probing function return.")) {
warn++;
} else {
/* Enable trace point */
tk = find_trace_kprobe("testprobe2", KPROBE_EVENT_SYSTEM);
if (WARN_ON_ONCE(tk == NULL)) {
pr_warn("error on getting 2nd new probe.\n");
if (WARN_ONCE(tk == NULL, "error on getting 2nd new probe.")) {
warn++;
} else {
file = find_trace_probe_file(tk, top_trace_array());
if (WARN_ON_ONCE(file == NULL)) {
pr_warn("error on getting probe file.\n");
if (WARN_ONCE(file == NULL, "error on getting probe file.")) {
warn++;
} else
enable_trace_kprobe(
@@ -2079,18 +2123,15 @@ static __init int kprobe_trace_self_tests_init(void)
/* Disable trace points before removing it */
tk = find_trace_kprobe("testprobe", KPROBE_EVENT_SYSTEM);
if (WARN_ON_ONCE(tk == NULL)) {
pr_warn("error on getting test probe.\n");
if (WARN_ONCE(tk == NULL, "error on getting test probe.")) {
warn++;
} else {
if (trace_kprobe_nhit(tk) != 1) {
pr_warn("incorrect number of testprobe hits\n");
if (WARN_ONCE(trace_kprobe_nhit(tk) != 1,
"incorrect number of testprobe hits."))
warn++;
}
file = find_trace_probe_file(tk, top_trace_array());
if (WARN_ON_ONCE(file == NULL)) {
pr_warn("error on getting probe file.\n");
if (WARN_ONCE(file == NULL, "error on getting probe file.")) {
warn++;
} else
disable_trace_kprobe(
@@ -2098,18 +2139,15 @@ static __init int kprobe_trace_self_tests_init(void)
}
tk = find_trace_kprobe("testprobe2", KPROBE_EVENT_SYSTEM);
if (WARN_ON_ONCE(tk == NULL)) {
pr_warn("error on getting 2nd test probe.\n");
if (WARN_ONCE(tk == NULL, "error on getting 2nd test probe.")) {
warn++;
} else {
if (trace_kprobe_nhit(tk) != 1) {
pr_warn("incorrect number of testprobe2 hits\n");
if (WARN_ONCE(trace_kprobe_nhit(tk) != 1,
"incorrect number of testprobe2 hits."))
warn++;
}
file = find_trace_probe_file(tk, top_trace_array());
if (WARN_ON_ONCE(file == NULL)) {
pr_warn("error on getting probe file.\n");
if (WARN_ONCE(file == NULL, "error on getting probe file.")) {
warn++;
} else
disable_trace_kprobe(
@@ -2117,23 +2155,15 @@ static __init int kprobe_trace_self_tests_init(void)
}
ret = create_or_delete_trace_kprobe("-:testprobe");
if (WARN_ON_ONCE(ret)) {
pr_warn("error on deleting a probe.\n");
if (WARN_ONCE(ret, "error on deleting a probe."))
warn++;
}
ret = create_or_delete_trace_kprobe("-:testprobe2");
if (WARN_ON_ONCE(ret)) {
pr_warn("error on deleting a probe.\n");
if (WARN_ONCE(ret, "error on deleting a probe."))
warn++;
}
end:
ret = dyn_events_release_all(&trace_kprobe_ops);
if (WARN_ON_ONCE(ret)) {
pr_warn("error on cleaning up probes.\n");
warn++;
}
/*
* Wait for the optimizer work to finish. Otherwise it might fiddle
* with probes in already freed __init text.