]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.19-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 10 Feb 2021 14:07:08 +0000 (15:07 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 10 Feb 2021 14:07:08 +0000 (15:07 +0100)
added patches:
tracing-kprobe-fix-to-support-kretprobe-events-on-unloaded-modules.patch

queue-4.19/tracing-kprobe-fix-to-support-kretprobe-events-on-unloaded-modules.patch [new file with mode: 0644]

diff --git a/queue-4.19/tracing-kprobe-fix-to-support-kretprobe-events-on-unloaded-modules.patch b/queue-4.19/tracing-kprobe-fix-to-support-kretprobe-events-on-unloaded-modules.patch
new file mode 100644 (file)
index 0000000..9a795af
--- /dev/null
@@ -0,0 +1,133 @@
+From 97c753e62e6c31a404183898d950d8c08d752dbd Mon Sep 17 00:00:00 2001
+From: Masami Hiramatsu <mhiramat@kernel.org>
+Date: Thu, 28 Jan 2021 00:37:51 +0900
+Subject: tracing/kprobe: Fix to support kretprobe events on unloaded modules
+
+From: Masami Hiramatsu <mhiramat@kernel.org>
+
+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 <Jianlin.Lv@arm.com>
+Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/kprobes.h     |    2 +-
+ kernel/kprobes.c            |   34 +++++++++++++++++++++++++---------
+ kernel/trace/trace_kprobe.c |    4 ++--
+ 3 files changed, 28 insertions(+), 12 deletions(-)
+
+--- a/include/linux/kprobes.h
++++ b/include/linux/kprobes.h
+@@ -245,7 +245,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
+@@ -1921,29 +1921,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
+@@ -112,9 +112,9 @@ bool trace_kprobe_on_func_entry(struct t
+ {
+       struct trace_kprobe *tk = (struct trace_kprobe *)call->data;
+-      return kprobe_on_func_entry(tk->rp.kp.addr,
++      return (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);
++                      tk->rp.kp.addr ? 0 : tk->rp.kp.offset) == 0);
+ }
+ bool trace_kprobe_error_injectable(struct trace_event_call *call)