From: Greg Kroah-Hartman Date: Wed, 10 Feb 2021 14:07:24 +0000 (+0100) Subject: 5.4-stable patches X-Git-Tag: v4.19.176~32 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8d4365674208565ded118f58fb62e06760a44bd6;p=thirdparty%2Fkernel%2Fstable-queue.git 5.4-stable patches added patches: tracing-kprobe-fix-to-support-kretprobe-events-on-unloaded-modules.patch --- diff --git a/queue-5.4/tracing-kprobe-fix-to-support-kretprobe-events-on-unloaded-modules.patch b/queue-5.4/tracing-kprobe-fix-to-support-kretprobe-events-on-unloaded-modules.patch new file mode 100644 index 00000000000..82208c014de --- /dev/null +++ b/queue-5.4/tracing-kprobe-fix-to-support-kretprobe-events-on-unloaded-modules.patch @@ -0,0 +1,148 @@ +From 97c753e62e6c31a404183898d950d8c08d752dbd Mon Sep 17 00:00:00 2001 +From: Masami Hiramatsu +Date: Thu, 28 Jan 2021 00:37:51 +0900 +Subject: tracing/kprobe: Fix to support kretprobe events on unloaded modules + +From: Masami Hiramatsu + +commit 97c753e62e6c31a404183898d950d8c08d752dbd upstream. + +Fix kprobe_on_func_entry() returns error code instead of false so that +register_kretprobe() can return an appropriate error code. + +append_trace_kprobe() expects the kprobe registration returns -ENOENT +when the target symbol is not found, and it checks whether the target +module is unloaded or not. If the target module doesn't exist, it +defers to probe the target symbol until the module is loaded. + +However, since register_kretprobe() returns -EINVAL instead of -ENOENT +in that case, it always fail on putting the kretprobe event on unloaded +modules. e.g. + +Kprobe event: +/sys/kernel/debug/tracing # echo p xfs:xfs_end_io >> kprobe_events +[ 16.515574] trace_kprobe: This probe might be able to register after target module is loaded. Continue. + +Kretprobe event: (p -> r) +/sys/kernel/debug/tracing # echo r xfs:xfs_end_io >> kprobe_events +sh: write error: Invalid argument +/sys/kernel/debug/tracing # cat error_log +[ 41.122514] trace_kprobe: error: Failed to register probe event + Command: r xfs:xfs_end_io + ^ + +To fix this bug, change kprobe_on_func_entry() to detect symbol lookup +failure and return -ENOENT in that case. Otherwise it returns -EINVAL +or 0 (succeeded, given address is on the entry). + +Link: https://lkml.kernel.org/r/161176187132.1067016.8118042342894378981.stgit@devnote2 + +Cc: stable@vger.kernel.org +Fixes: 59158ec4aef7 ("tracing/kprobes: Check the probe on unloaded module correctly") +Reported-by: Jianlin Lv +Signed-off-by: Masami Hiramatsu +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: Greg Kroah-Hartman + +--- + include/linux/kprobes.h | 2 +- + kernel/kprobes.c | 34 +++++++++++++++++++++++++--------- + kernel/trace/trace_kprobe.c | 10 ++++++---- + 3 files changed, 32 insertions(+), 14 deletions(-) + +--- a/include/linux/kprobes.h ++++ b/include/linux/kprobes.h +@@ -232,7 +232,7 @@ extern void kprobes_inc_nmissed_count(st + extern bool arch_within_kprobe_blacklist(unsigned long addr); + extern int arch_populate_kprobe_blacklist(void); + extern bool arch_kprobe_on_func_entry(unsigned long offset); +-extern bool kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset); ++extern int kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset); + + extern bool within_kprobe_blacklist(unsigned long addr); + extern int kprobe_add_ksym_blacklist(unsigned long entry); +--- a/kernel/kprobes.c ++++ b/kernel/kprobes.c +@@ -1948,29 +1948,45 @@ bool __weak arch_kprobe_on_func_entry(un + return !offset; + } + +-bool kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset) ++/** ++ * kprobe_on_func_entry() -- check whether given address is function entry ++ * @addr: Target address ++ * @sym: Target symbol name ++ * @offset: The offset from the symbol or the address ++ * ++ * This checks whether the given @addr+@offset or @sym+@offset is on the ++ * function entry address or not. ++ * This returns 0 if it is the function entry, or -EINVAL if it is not. ++ * And also it returns -ENOENT if it fails the symbol or address lookup. ++ * Caller must pass @addr or @sym (either one must be NULL), or this ++ * returns -EINVAL. ++ */ ++int kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset) + { + kprobe_opcode_t *kp_addr = _kprobe_addr(addr, sym, offset); + + if (IS_ERR(kp_addr)) +- return false; ++ return PTR_ERR(kp_addr); + +- if (!kallsyms_lookup_size_offset((unsigned long)kp_addr, NULL, &offset) || +- !arch_kprobe_on_func_entry(offset)) +- return false; ++ if (!kallsyms_lookup_size_offset((unsigned long)kp_addr, NULL, &offset)) ++ return -ENOENT; + +- return true; ++ if (!arch_kprobe_on_func_entry(offset)) ++ return -EINVAL; ++ ++ return 0; + } + + int register_kretprobe(struct kretprobe *rp) + { +- int ret = 0; ++ int ret; + struct kretprobe_instance *inst; + int i; + void *addr; + +- if (!kprobe_on_func_entry(rp->kp.addr, rp->kp.symbol_name, rp->kp.offset)) +- return -EINVAL; ++ ret = kprobe_on_func_entry(rp->kp.addr, rp->kp.symbol_name, rp->kp.offset); ++ if (ret) ++ return ret; + + /* If only rp->kp.addr is specified, check reregistering kprobes */ + if (rp->kp.addr && check_kprobe_rereg(&rp->kp)) +--- a/kernel/trace/trace_kprobe.c ++++ b/kernel/trace/trace_kprobe.c +@@ -220,9 +220,9 @@ bool trace_kprobe_on_func_entry(struct t + { + struct trace_kprobe *tk = trace_kprobe_primary_from_call(call); + +- return tk ? kprobe_on_func_entry(tk->rp.kp.addr, ++ return tk ? (kprobe_on_func_entry(tk->rp.kp.addr, + tk->rp.kp.addr ? NULL : tk->rp.kp.symbol_name, +- tk->rp.kp.addr ? 0 : tk->rp.kp.offset) : false; ++ tk->rp.kp.addr ? 0 : tk->rp.kp.offset) == 0) : false; + } + + bool trace_kprobe_error_injectable(struct trace_event_call *call) +@@ -811,9 +811,11 @@ static int trace_kprobe_create(int argc, + trace_probe_log_err(0, BAD_PROBE_ADDR); + goto parse_error; + } +- if (kprobe_on_func_entry(NULL, symbol, offset)) ++ ret = kprobe_on_func_entry(NULL, symbol, offset); ++ if (ret == 0) + flags |= TPARG_FL_FENTRY; +- if (offset && is_return && !(flags & TPARG_FL_FENTRY)) { ++ /* Defer the ENOENT case until register kprobe */ ++ if (ret == -EINVAL && is_return) { + trace_probe_log_err(0, BAD_RETPROBE); + goto parse_error; + }