2091 lines · 50.2 KB · C
RawDownload
// 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){	strscpy(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 2091.