// SPDX-License-Identifier: GPL-2.0/* * Kprobes-based tracing events * * Created by Masami Hiramatsu <mhiramat@redhat.com> * */#define pr_fmt(fmt) "trace_kprobe: " fmt #include <linux/bpf-cgroup.h>#include <linux/security.h>#include <linux/module.h>#include <linux/uaccess.h>#include <linux/rculist.h>#include <linux/error-injection.h> #include <asm/setup.h> /* for COMMAND_LINE_SIZE */ #include "trace_dynevent.h"#include "trace_kprobe_selftest.h"#include "trace_probe.h"#include "trace_probe_tmpl.h"#include "trace_probe_kernel.h" #define KPROBE_EVENT_SYSTEM "kprobes"#define KRETPROBE_MAXACTIVE_MAX 4096 /* Kprobe early definition from command line */static char kprobe_boot_events_buf[COMMAND_LINE_SIZE] __initdata; static int __init set_kprobe_boot_events(char *str){ strlcpy(kprobe_boot_events_buf, str, COMMAND_LINE_SIZE); disable_tracing_selftest("running kprobe events"); return 1;}__setup("kprobe_event=", set_kprobe_boot_events); static int trace_kprobe_create(const char *raw_command);static int trace_kprobe_show(struct seq_file *m, struct dyn_event *ev);static int trace_kprobe_release(struct dyn_event *ev);static bool trace_kprobe_is_busy(struct dyn_event *ev);static bool trace_kprobe_match(const char *system, const char *event, int argc, const char **argv, struct dyn_event *ev); static struct dyn_event_operations trace_kprobe_ops = { .create = trace_kprobe_create, .show = trace_kprobe_show, .is_busy = trace_kprobe_is_busy, .free = trace_kprobe_release, .match = trace_kprobe_match,}; /* * Kprobe event core functions */struct trace_kprobe { struct dyn_event devent; struct kretprobe rp; /* Use rp.kp for kprobe use */ unsigned long __percpu *nhit; const char *symbol; /* symbol name */ struct trace_probe tp;}; static bool is_trace_kprobe(struct dyn_event *ev){ return ev->ops == &trace_kprobe_ops;} static struct trace_kprobe *to_trace_kprobe(struct dyn_event *ev){ return container_of(ev, struct trace_kprobe, devent);} /** * for_each_trace_kprobe - iterate over the trace_kprobe list * @pos: the struct trace_kprobe * for each entry * @dpos: the struct dyn_event * to use as a loop cursor */#define for_each_trace_kprobe(pos, dpos) \ for_each_dyn_event(dpos) \ if (is_trace_kprobe(dpos) && (pos = to_trace_kprobe(dpos))) static nokprobe_inline bool trace_kprobe_is_return(struct trace_kprobe *tk){ return tk->rp.handler != NULL;} static nokprobe_inline const char *trace_kprobe_symbol(struct trace_kprobe *tk){ return tk->symbol ? tk->symbol : "unknown";} static nokprobe_inline unsigned long trace_kprobe_offset(struct trace_kprobe *tk){ return tk->rp.kp.offset;} static nokprobe_inline bool trace_kprobe_has_gone(struct trace_kprobe *tk){ return kprobe_gone(&tk->rp.kp);} static nokprobe_inline bool trace_kprobe_within_module(struct trace_kprobe *tk, struct module *mod){ int len = strlen(module_name(mod)); const char *name = trace_kprobe_symbol(tk); return strncmp(module_name(mod), name, len) == 0 && name[len] == ':';} static nokprobe_inline bool trace_kprobe_module_exist(struct trace_kprobe *tk){ char *p; bool ret; if (!tk->symbol) return false; p = strchr(tk->symbol, ':'); if (!p) return true; *p = '\0'; rcu_read_lock_sched(); ret = !!find_module(tk->symbol); rcu_read_unlock_sched(); *p = ':'; return ret;} static bool trace_kprobe_is_busy(struct dyn_event *ev){ struct trace_kprobe *tk = to_trace_kprobe(ev); return trace_probe_is_enabled(&tk->tp);} static bool trace_kprobe_match_command_head(struct trace_kprobe *tk, int argc, const char **argv){ char buf[MAX_ARGSTR_LEN + 1]; if (!argc) return true; if (!tk->symbol) snprintf(buf, sizeof(buf), "0x%p", tk->rp.kp.addr); else if (tk->rp.kp.offset) snprintf(buf, sizeof(buf), "%s+%u", trace_kprobe_symbol(tk), tk->rp.kp.offset); else snprintf(buf, sizeof(buf), "%s", trace_kprobe_symbol(tk)); if (strcmp(buf, argv[0])) return false; argc--; argv++; return trace_probe_match_command_args(&tk->tp, argc, argv);} static bool trace_kprobe_match(const char *system, const char *event, int argc, const char **argv, struct dyn_event *ev){ struct trace_kprobe *tk = to_trace_kprobe(ev); return (event[0] == '\0' || strcmp(trace_probe_name(&tk->tp), event) == 0) && (!system || strcmp(trace_probe_group_name(&tk->tp), system) == 0) && trace_kprobe_match_command_head(tk, argc, argv);} static nokprobe_inline unsigned long trace_kprobe_nhit(struct trace_kprobe *tk){ unsigned long nhit = 0; int cpu; for_each_possible_cpu(cpu) nhit += *per_cpu_ptr(tk->nhit, cpu); return nhit;} static nokprobe_inline bool trace_kprobe_is_registered(struct trace_kprobe *tk){ return !(list_empty(&tk->rp.kp.list) && hlist_unhashed(&tk->rp.kp.hlist));} /* Return 0 if it fails to find the symbol address */static nokprobe_inlineunsigned 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;} static nokprobe_inline struct trace_kprobe *trace_kprobe_primary_from_call(struct trace_event_call *call){ struct trace_probe *tp; tp = trace_probe_primary_from_call(call); if (WARN_ON_ONCE(!tp)) return NULL; return container_of(tp, struct trace_kprobe, tp);} bool trace_kprobe_on_func_entry(struct trace_event_call *call){ struct trace_kprobe *tk = trace_kprobe_primary_from_call(call); 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) == 0) : false;} bool trace_kprobe_error_injectable(struct trace_event_call *call){ struct trace_kprobe *tk = trace_kprobe_primary_from_call(call); return tk ? within_error_injection_list(trace_kprobe_address(tk)) : false;} static int register_kprobe_event(struct trace_kprobe *tk);static int unregister_kprobe_event(struct trace_kprobe *tk); static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs);static int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs); static void free_trace_kprobe(struct trace_kprobe *tk){ if (tk) { trace_probe_cleanup(&tk->tp); kfree(tk->symbol); free_percpu(tk->nhit); kfree(tk); }} /* * Allocate new trace_probe and initialize it (including kprobes). */static struct trace_kprobe *alloc_trace_kprobe(const char *group, const char *event, void *addr, const char *symbol, unsigned long offs, int maxactive, int nargs, bool is_return){ struct trace_kprobe *tk; int ret = -ENOMEM; tk = kzalloc(struct_size(tk, tp.args, nargs), GFP_KERNEL); if (!tk) return ERR_PTR(ret); tk->nhit = alloc_percpu(unsigned long); if (!tk->nhit) goto error; if (symbol) { tk->symbol = kstrdup(symbol, GFP_KERNEL); if (!tk->symbol) goto error; tk->rp.kp.symbol_name = tk->symbol; tk->rp.kp.offset = offs; } else tk->rp.kp.addr = addr; if (is_return) tk->rp.handler = kretprobe_dispatcher; else tk->rp.kp.pre_handler = kprobe_dispatcher; tk->rp.maxactive = maxactive; INIT_HLIST_NODE(&tk->rp.kp.hlist); INIT_LIST_HEAD(&tk->rp.kp.list); ret = trace_probe_init(&tk->tp, event, group, false); if (ret < 0) goto error; dyn_event_init(&tk->devent, &trace_kprobe_ops); return tk;error: free_trace_kprobe(tk); return ERR_PTR(ret);} static struct trace_kprobe *find_trace_kprobe(const char *event, const char *group){ struct dyn_event *pos; struct trace_kprobe *tk; for_each_trace_kprobe(tk, pos) if (strcmp(trace_probe_name(&tk->tp), event) == 0 && strcmp(trace_probe_group_name(&tk->tp), group) == 0) return tk; return NULL;} static inline int __enable_trace_kprobe(struct trace_kprobe *tk){ int ret = 0; if (trace_kprobe_is_registered(tk) && !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;} static void __disable_trace_kprobe(struct trace_probe *tp){ struct trace_kprobe *tk; list_for_each_entry(tk, trace_probe_probe_list(tp), tp.list) { if (!trace_kprobe_is_registered(tk)) continue; if (trace_kprobe_is_return(tk)) disable_kretprobe(&tk->rp); else disable_kprobe(&tk->rp.kp); }} /* * Enable trace_probe * if the file is NULL, enable "perf" handler, or enable "trace" handler. */static int enable_trace_kprobe(struct trace_event_call *call, struct trace_event_file *file){ struct trace_probe *tp; struct trace_kprobe *tk; bool enabled; int ret = 0; tp = trace_probe_primary_from_call(call); if (WARN_ON_ONCE(!tp)) return -ENODEV; enabled = trace_probe_is_enabled(tp); /* This also changes "enabled" state */ if (file) { ret = trace_probe_add_file(tp, file); if (ret) return ret; } else trace_probe_set_flag(tp, TP_FLAG_PROFILE); if (enabled) return 0; list_for_each_entry(tk, trace_probe_probe_list(tp), tp.list) { if (trace_kprobe_has_gone(tk)) continue; ret = __enable_trace_kprobe(tk); if (ret) break; enabled = true; } if (ret) { /* Failed to enable one of them. Roll back all */ if (enabled) __disable_trace_kprobe(tp); if (file) trace_probe_remove_file(tp, file); else trace_probe_clear_flag(tp, TP_FLAG_PROFILE); } return ret;} /* * Disable trace_probe * if the file is NULL, disable "perf" handler, or disable "trace" handler. */static int disable_trace_kprobe(struct trace_event_call *call, struct trace_event_file *file){ struct trace_probe *tp; tp = trace_probe_primary_from_call(call); if (WARN_ON_ONCE(!tp)) return -ENODEV; if (file) { if (!trace_probe_get_file_link(tp, file)) return -ENOENT; if (!trace_probe_has_single_file(tp)) goto out; trace_probe_clear_flag(tp, TP_FLAG_TRACE); } else trace_probe_clear_flag(tp, TP_FLAG_PROFILE); if (!trace_probe_is_enabled(tp)) __disable_trace_kprobe(tp); out: if (file) /* * Synchronization is done in below function. For perf event, * file == NULL and perf_trace_event_unreg() calls * tracepoint_synchronize_unregister() to ensure synchronize * event. We don't need to care about it. */ trace_probe_remove_file(tp, file); return 0;} #if defined(CONFIG_DYNAMIC_FTRACE) && \ !defined(CONFIG_KPROBE_EVENTS_ON_NOTRACE)static bool __within_notrace_func(unsigned long addr){ unsigned long offset, size; 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);} static bool within_notrace_func(struct trace_kprobe *tk){ unsigned long addr = trace_kprobe_address(tk); char symname[KSYM_NAME_LEN], *p; if (!__within_notrace_func(addr)) return false; /* Check if the address is on a suffixed-symbol */ if (!lookup_symbol_name(addr, symname)) { p = strchr(symname, '.'); if (!p) return true; *p = '\0'; addr = (unsigned long)kprobe_lookup_name(symname, 0); if (addr) return __within_notrace_func(addr); } return true;}#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){ int i, ret; ret = security_locked_down(LOCKDOWN_KPROBES); if (ret) return ret; if (trace_kprobe_is_registered(tk)) 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++) { ret = traceprobe_update_arg(&tk->tp.args[i]); if (ret) return ret; } Lines 1–500 of 1991. Load more
// SPDX-License-Identifier: GPL-2.0 /* * Kprobes-based tracing events * * Created by Masami Hiramatsu <mhiramat@redhat.com> * */ #define pr_fmt(fmt) "trace_kprobe: " fmt #include <linux/bpf-cgroup.h> #include <linux/security.h> #include <linux/module.h> #include <linux/uaccess.h> #include <linux/rculist.h> #include <linux/error-injection.h> #include <asm/setup.h> /* for COMMAND_LINE_SIZE */ #include "trace_dynevent.h" #include "trace_kprobe_selftest.h" #include "trace_probe.h" #include "trace_probe_tmpl.h" #include "trace_probe_kernel.h" #define KPROBE_EVENT_SYSTEM "kprobes" #define KRETPROBE_MAXACTIVE_MAX 4096 /* Kprobe early definition from command line */ static char kprobe_boot_events_buf[COMMAND_LINE_SIZE] __initdata; static int __init set_kprobe_boot_events(char *str) { strlcpy(kprobe_boot_events_buf, str, COMMAND_LINE_SIZE); disable_tracing_selftest("running kprobe events"); return 1; } __setup("kprobe_event=", set_kprobe_boot_events); static int trace_kprobe_create(const char *raw_command); static int trace_kprobe_show(struct seq_file *m, struct dyn_event *ev); static int trace_kprobe_release(struct dyn_event *ev); static bool trace_kprobe_is_busy(struct dyn_event *ev); static bool trace_kprobe_match(const char *system, const char *event, int argc, const char **argv, struct dyn_event *ev); static struct dyn_event_operations trace_kprobe_ops = { .create = trace_kprobe_create, .show = trace_kprobe_show, .is_busy = trace_kprobe_is_busy, .free = trace_kprobe_release, .match = trace_kprobe_match, }; /* * Kprobe event core functions */ struct trace_kprobe { struct dyn_event devent; struct kretprobe rp; /* Use rp.kp for kprobe use */ unsigned long __percpu *nhit; const char *symbol; /* symbol name */ struct trace_probe tp; }; static bool is_trace_kprobe(struct dyn_event *ev) { return ev->ops == &trace_kprobe_ops; } static struct trace_kprobe *to_trace_kprobe(struct dyn_event *ev) { return container_of(ev, struct trace_kprobe, devent); } /** * for_each_trace_kprobe - iterate over the trace_kprobe list * @pos: the struct trace_kprobe * for each entry * @dpos: the struct dyn_event * to use as a loop cursor */ #define for_each_trace_kprobe(pos, dpos) \ for_each_dyn_event(dpos) \ if (is_trace_kprobe(dpos) && (pos = to_trace_kprobe(dpos))) static nokprobe_inline bool trace_kprobe_is_return(struct trace_kprobe *tk) { return tk->rp.handler != NULL; } static nokprobe_inline const char *trace_kprobe_symbol(struct trace_kprobe *tk) { return tk->symbol ? tk->symbol : "unknown"; } static nokprobe_inline unsigned long trace_kprobe_offset(struct trace_kprobe *tk) { return tk->rp.kp.offset; } static nokprobe_inline bool trace_kprobe_has_gone(struct trace_kprobe *tk) { return kprobe_gone(&tk->rp.kp); } static nokprobe_inline bool trace_kprobe_within_module(struct trace_kprobe *tk, struct module *mod) { int len = strlen(module_name(mod)); const char *name = trace_kprobe_symbol(tk); return strncmp(module_name(mod), name, len) == 0 && name[len] == ':'; } static nokprobe_inline bool trace_kprobe_module_exist(struct trace_kprobe *tk) { char *p; bool ret; if (!tk->symbol) return false; p = strchr(tk->symbol, ':'); if (!p) return true; *p = '\0'; rcu_read_lock_sched(); ret = !!find_module(tk->symbol); rcu_read_unlock_sched(); *p = ':'; return ret; } static bool trace_kprobe_is_busy(struct dyn_event *ev) { struct trace_kprobe *tk = to_trace_kprobe(ev); return trace_probe_is_enabled(&tk->tp); } static bool trace_kprobe_match_command_head(struct trace_kprobe *tk, int argc, const char **argv) { char buf[MAX_ARGSTR_LEN + 1]; if (!argc) return true; if (!tk->symbol) snprintf(buf, sizeof(buf), "0x%p", tk->rp.kp.addr); else if (tk->rp.kp.offset) snprintf(buf, sizeof(buf), "%s+%u", trace_kprobe_symbol(tk), tk->rp.kp.offset); else snprintf(buf, sizeof(buf), "%s", trace_kprobe_symbol(tk)); if (strcmp(buf, argv[0])) return false; argc--; argv++; return trace_probe_match_command_args(&tk->tp, argc, argv); } static bool trace_kprobe_match(const char *system, const char *event, int argc, const char **argv, struct dyn_event *ev) { struct trace_kprobe *tk = to_trace_kprobe(ev); return (event[0] == '\0' || strcmp(trace_probe_name(&tk->tp), event) == 0) && (!system || strcmp(trace_probe_group_name(&tk->tp), system) == 0) && trace_kprobe_match_command_head(tk, argc, argv); } static nokprobe_inline unsigned long trace_kprobe_nhit(struct trace_kprobe *tk) { unsigned long nhit = 0; int cpu; for_each_possible_cpu(cpu) nhit += *per_cpu_ptr(tk->nhit, cpu); return nhit; } static nokprobe_inline bool trace_kprobe_is_registered(struct trace_kprobe *tk) { return !(list_empty(&tk->rp.kp.list) && hlist_unhashed(&tk->rp.kp.hlist)); } /* 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; } static nokprobe_inline struct trace_kprobe * trace_kprobe_primary_from_call(struct trace_event_call *call) { struct trace_probe *tp; tp = trace_probe_primary_from_call(call); if (WARN_ON_ONCE(!tp)) return NULL; return container_of(tp, struct trace_kprobe, tp); } bool trace_kprobe_on_func_entry(struct trace_event_call *call) { struct trace_kprobe *tk = trace_kprobe_primary_from_call(call); 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) == 0) : false; } bool trace_kprobe_error_injectable(struct trace_event_call *call) { struct trace_kprobe *tk = trace_kprobe_primary_from_call(call); return tk ? within_error_injection_list(trace_kprobe_address(tk)) : false; } static int register_kprobe_event(struct trace_kprobe *tk); static int unregister_kprobe_event(struct trace_kprobe *tk); static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs); static int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs); static void free_trace_kprobe(struct trace_kprobe *tk) { if (tk) { trace_probe_cleanup(&tk->tp); kfree(tk->symbol); free_percpu(tk->nhit); kfree(tk); } } /* * Allocate new trace_probe and initialize it (including kprobes). */ static struct trace_kprobe *alloc_trace_kprobe(const char *group, const char *event, void *addr, const char *symbol, unsigned long offs, int maxactive, int nargs, bool is_return) { struct trace_kprobe *tk; int ret = -ENOMEM; tk = kzalloc(struct_size(tk, tp.args, nargs), GFP_KERNEL); if (!tk) return ERR_PTR(ret); tk->nhit = alloc_percpu(unsigned long); if (!tk->nhit) goto error; if (symbol) { tk->symbol = kstrdup(symbol, GFP_KERNEL); if (!tk->symbol) goto error; tk->rp.kp.symbol_name = tk->symbol; tk->rp.kp.offset = offs; } else tk->rp.kp.addr = addr; if (is_return) tk->rp.handler = kretprobe_dispatcher; else tk->rp.kp.pre_handler = kprobe_dispatcher; tk->rp.maxactive = maxactive; INIT_HLIST_NODE(&tk->rp.kp.hlist); INIT_LIST_HEAD(&tk->rp.kp.list); ret = trace_probe_init(&tk->tp, event, group, false); if (ret < 0) goto error; dyn_event_init(&tk->devent, &trace_kprobe_ops); return tk; error: free_trace_kprobe(tk); return ERR_PTR(ret); } static struct trace_kprobe *find_trace_kprobe(const char *event, const char *group) { struct dyn_event *pos; struct trace_kprobe *tk; for_each_trace_kprobe(tk, pos) if (strcmp(trace_probe_name(&tk->tp), event) == 0 && strcmp(trace_probe_group_name(&tk->tp), group) == 0) return tk; return NULL; } static inline int __enable_trace_kprobe(struct trace_kprobe *tk) { int ret = 0; if (trace_kprobe_is_registered(tk) && !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; } static void __disable_trace_kprobe(struct trace_probe *tp) { struct trace_kprobe *tk; list_for_each_entry(tk, trace_probe_probe_list(tp), tp.list) { if (!trace_kprobe_is_registered(tk)) continue; if (trace_kprobe_is_return(tk)) disable_kretprobe(&tk->rp); else disable_kprobe(&tk->rp.kp); } } /* * Enable trace_probe * if the file is NULL, enable "perf" handler, or enable "trace" handler. */ static int enable_trace_kprobe(struct trace_event_call *call, struct trace_event_file *file) { struct trace_probe *tp; struct trace_kprobe *tk; bool enabled; int ret = 0; tp = trace_probe_primary_from_call(call); if (WARN_ON_ONCE(!tp)) return -ENODEV; enabled = trace_probe_is_enabled(tp); /* This also changes "enabled" state */ if (file) { ret = trace_probe_add_file(tp, file); if (ret) return ret; } else trace_probe_set_flag(tp, TP_FLAG_PROFILE); if (enabled) return 0; list_for_each_entry(tk, trace_probe_probe_list(tp), tp.list) { if (trace_kprobe_has_gone(tk)) continue; ret = __enable_trace_kprobe(tk); if (ret) break; enabled = true; } if (ret) { /* Failed to enable one of them. Roll back all */ if (enabled) __disable_trace_kprobe(tp); if (file) trace_probe_remove_file(tp, file); else trace_probe_clear_flag(tp, TP_FLAG_PROFILE); } return ret; } /* * Disable trace_probe * if the file is NULL, disable "perf" handler, or disable "trace" handler. */ static int disable_trace_kprobe(struct trace_event_call *call, struct trace_event_file *file) { struct trace_probe *tp; tp = trace_probe_primary_from_call(call); if (WARN_ON_ONCE(!tp)) return -ENODEV; if (file) { if (!trace_probe_get_file_link(tp, file)) return -ENOENT; if (!trace_probe_has_single_file(tp)) goto out; trace_probe_clear_flag(tp, TP_FLAG_TRACE); } else trace_probe_clear_flag(tp, TP_FLAG_PROFILE); if (!trace_probe_is_enabled(tp)) __disable_trace_kprobe(tp); out: if (file) /* * Synchronization is done in below function. For perf event, * file == NULL and perf_trace_event_unreg() calls * tracepoint_synchronize_unregister() to ensure synchronize * event. We don't need to care about it. */ trace_probe_remove_file(tp, file); return 0; } #if defined(CONFIG_DYNAMIC_FTRACE) && \ !defined(CONFIG_KPROBE_EVENTS_ON_NOTRACE) static bool __within_notrace_func(unsigned long addr) { unsigned long offset, size; 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); } static bool within_notrace_func(struct trace_kprobe *tk) { unsigned long addr = trace_kprobe_address(tk); char symname[KSYM_NAME_LEN], *p; if (!__within_notrace_func(addr)) return false; /* Check if the address is on a suffixed-symbol */ if (!lookup_symbol_name(addr, symname)) { p = strchr(symname, '.'); if (!p) return true; *p = '\0'; addr = (unsigned long)kprobe_lookup_name(symname, 0); if (addr) return __within_notrace_func(addr); } return true; } #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) { int i, ret; ret = security_locked_down(LOCKDOWN_KPROBES); if (ret) return ret; if (trace_kprobe_is_registered(tk)) 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++) { ret = traceprobe_update_arg(&tk->tp.args[i]); if (ret) return ret; }