Merge tag 'trace-v4.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt...
[sfrench/cifs-2.6.git] / kernel / trace / trace_kprobe.c
index e9d99463e5dffe82bcc42f0ea75392d0528ce2fe..c30032367aab4a5c83a013d615830cda97a82f8a 100644 (file)
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Kprobes-based tracing events
  *
  * Created by Masami Hiramatsu <mhiramat@redhat.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #define pr_fmt(fmt)    "trace_kprobe: " fmt
 
@@ -23,6 +12,7 @@
 #include <linux/rculist.h>
 #include <linux/error-injection.h>
 
+#include "trace_kprobe_selftest.h"
 #include "trace_probe.h"
 
 #define KPROBE_EVENT_SYSTEM "kprobes"
@@ -87,6 +77,23 @@ static nokprobe_inline unsigned long trace_kprobe_nhit(struct trace_kprobe *tk)
        return nhit;
 }
 
+/* Return 0 if it fails to find the symbol address */
+static nokprobe_inline
+unsigned long trace_kprobe_address(struct trace_kprobe *tk)
+{
+       unsigned long addr;
+
+       if (tk->symbol) {
+               addr = (unsigned long)
+                       kallsyms_lookup_name(trace_kprobe_symbol(tk));
+               if (addr)
+                       addr += tk->rp.kp.offset;
+       } else {
+               addr = (unsigned long)tk->rp.kp.addr;
+       }
+       return addr;
+}
+
 bool trace_kprobe_on_func_entry(struct trace_event_call *call)
 {
        struct trace_kprobe *tk = (struct trace_kprobe *)call->data;
@@ -99,16 +106,8 @@ bool trace_kprobe_on_func_entry(struct trace_event_call *call)
 bool trace_kprobe_error_injectable(struct trace_event_call *call)
 {
        struct trace_kprobe *tk = (struct trace_kprobe *)call->data;
-       unsigned long addr;
 
-       if (tk->symbol) {
-               addr = (unsigned long)
-                       kallsyms_lookup_name(trace_kprobe_symbol(tk));
-               addr += tk->rp.kp.offset;
-       } else {
-               addr = (unsigned long)tk->rp.kp.addr;
-       }
-       return within_error_injection_list(addr);
+       return within_error_injection_list(trace_kprobe_address(tk));
 }
 
 static int register_kprobe_event(struct trace_kprobe *tk);
@@ -393,6 +392,20 @@ static struct trace_kprobe *find_trace_kprobe(const char *event,
        return NULL;
 }
 
+static inline int __enable_trace_kprobe(struct trace_kprobe *tk)
+{
+       int ret = 0;
+
+       if (trace_probe_is_registered(&tk->tp) && !trace_kprobe_has_gone(tk)) {
+               if (trace_kprobe_is_return(tk))
+                       ret = enable_kretprobe(&tk->rp);
+               else
+                       ret = enable_kprobe(&tk->rp.kp);
+       }
+
+       return ret;
+}
+
 /*
  * Enable trace_probe
  * if the file is NULL, enable "perf" handler, or enable "trace" handler.
@@ -400,7 +413,7 @@ static struct trace_kprobe *find_trace_kprobe(const char *event,
 static int
 enable_trace_kprobe(struct trace_kprobe *tk, struct trace_event_file *file)
 {
-       struct event_file_link *link = NULL;
+       struct event_file_link *link;
        int ret = 0;
 
        if (file) {
@@ -414,26 +427,18 @@ enable_trace_kprobe(struct trace_kprobe *tk, struct trace_event_file *file)
                list_add_tail_rcu(&link->list, &tk->tp.files);
 
                tk->tp.flags |= TP_FLAG_TRACE;
-       } else
-               tk->tp.flags |= TP_FLAG_PROFILE;
-
-       if (trace_probe_is_registered(&tk->tp) && !trace_kprobe_has_gone(tk)) {
-               if (trace_kprobe_is_return(tk))
-                       ret = enable_kretprobe(&tk->rp);
-               else
-                       ret = enable_kprobe(&tk->rp.kp);
-       }
-
-       if (ret) {
-               if (file) {
-                       /* Notice the if is true on not WARN() */
-                       if (!WARN_ON_ONCE(!link))
-                               list_del_rcu(&link->list);
+               ret = __enable_trace_kprobe(tk);
+               if (ret) {
+                       list_del_rcu(&link->list);
                        kfree(link);
                        tk->tp.flags &= ~TP_FLAG_TRACE;
-               } else {
-                       tk->tp.flags &= ~TP_FLAG_PROFILE;
                }
+
+       } else {
+               tk->tp.flags |= TP_FLAG_PROFILE;
+               ret = __enable_trace_kprobe(tk);
+               if (ret)
+                       tk->tp.flags &= ~TP_FLAG_PROFILE;
        }
  out:
        return ret;
@@ -498,6 +503,29 @@ disable_trace_kprobe(struct trace_kprobe *tk, struct trace_event_file *file)
        return ret;
 }
 
+#if defined(CONFIG_KPROBES_ON_FTRACE) && \
+       !defined(CONFIG_KPROBE_EVENTS_ON_NOTRACE)
+static bool within_notrace_func(struct trace_kprobe *tk)
+{
+       unsigned long offset, size, addr;
+
+       addr = trace_kprobe_address(tk);
+       if (!addr || !kallsyms_lookup_size_offset(addr, &size, &offset))
+               return false;
+
+       /* Get the entry address of the target function */
+       addr -= offset;
+
+       /*
+        * Since ftrace_location_range() does inclusive range check, we need
+        * to subtract 1 byte from the end address.
+        */
+       return !ftrace_location_range(addr, addr + size - 1);
+}
+#else
+#define within_notrace_func(tk)        (false)
+#endif
+
 /* Internal register function - just handle k*probes and flags */
 static int __register_trace_kprobe(struct trace_kprobe *tk)
 {
@@ -506,6 +534,12 @@ static int __register_trace_kprobe(struct trace_kprobe *tk)
        if (trace_probe_is_registered(&tk->tp))
                return -EINVAL;
 
+       if (within_notrace_func(tk)) {
+               pr_warn("Could not probe notrace function %s\n",
+                       trace_kprobe_symbol(tk));
+               return -EINVAL;
+       }
+
        for (i = 0; i < tk->tp.nr_args; i++)
                traceprobe_update_arg(&tk->tp.args[i]);
 
@@ -1547,17 +1581,6 @@ fs_initcall(init_kprobe_trace);
 
 
 #ifdef CONFIG_FTRACE_STARTUP_TEST
-/*
- * The "__used" keeps gcc from removing the function symbol
- * from the kallsyms table. 'noinline' makes sure that there
- * isn't an inlined version used by the test method below
- */
-static __used __init noinline int
-kprobe_trace_selftest_target(int a1, int a2, int a3, int a4, int a5, int a6)
-{
-       return a1 + a2 + a3 + a4 + a5 + a6;
-}
-
 static __init struct trace_event_file *
 find_trace_probe_file(struct trace_kprobe *tk, struct trace_array *tr)
 {