4160 lines · 96.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * event tracer * * Copyright (C) 2008 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * *  - Added format output of fields of the trace point. *    This was based off of work by Tom Zanussi <tzanussi@gmail.com>. * */ #define pr_fmt(fmt) fmt #include <linux/workqueue.h>#include <linux/security.h>#include <linux/spinlock.h>#include <linux/kthread.h>#include <linux/tracefs.h>#include <linux/uaccess.h>#include <linux/module.h>#include <linux/ctype.h>#include <linux/sort.h>#include <linux/slab.h>#include <linux/delay.h> #include <trace/events/sched.h>#include <trace/syscall.h> #include <asm/setup.h> #include "trace_output.h" #undef TRACE_SYSTEM#define TRACE_SYSTEM "TRACE_SYSTEM" DEFINE_MUTEX(event_mutex); LIST_HEAD(ftrace_events);static LIST_HEAD(ftrace_generic_fields);static LIST_HEAD(ftrace_common_fields);static bool eventdir_initialized; static LIST_HEAD(module_strings); struct module_string {	struct list_head	next;	struct module		*module;	char			*str;}; #define GFP_TRACE (GFP_KERNEL | __GFP_ZERO) static struct kmem_cache *field_cachep;static struct kmem_cache *file_cachep; static inline int system_refcount(struct event_subsystem *system){	return system->ref_count;} static int system_refcount_inc(struct event_subsystem *system){	return system->ref_count++;} static int system_refcount_dec(struct event_subsystem *system){	return --system->ref_count;} /* Double loops, do not use break, only goto's work */#define do_for_each_event_file(tr, file)			\	list_for_each_entry(tr, &ftrace_trace_arrays, list) {	\		list_for_each_entry(file, &tr->events, list) #define do_for_each_event_file_safe(tr, file)			\	list_for_each_entry(tr, &ftrace_trace_arrays, list) {	\		struct trace_event_file *___n;				\		list_for_each_entry_safe(file, ___n, &tr->events, list) #define while_for_each_event_file()		\	} static struct ftrace_event_field *__find_event_field(struct list_head *head, char *name){	struct ftrace_event_field *field; 	list_for_each_entry(field, head, link) {		if (!strcmp(field->name, name))			return field;	} 	return NULL;} struct ftrace_event_field *trace_find_event_field(struct trace_event_call *call, char *name){	struct ftrace_event_field *field;	struct list_head *head; 	head = trace_get_fields(call);	field = __find_event_field(head, name);	if (field)		return field; 	field = __find_event_field(&ftrace_generic_fields, name);	if (field)		return field; 	return __find_event_field(&ftrace_common_fields, name);} static int __trace_define_field(struct list_head *head, const char *type,				const char *name, int offset, int size,				int is_signed, int filter_type, int len){	struct ftrace_event_field *field; 	field = kmem_cache_alloc(field_cachep, GFP_TRACE);	if (!field)		return -ENOMEM; 	field->name = name;	field->type = type; 	if (filter_type == FILTER_OTHER)		field->filter_type = filter_assign_type(type);	else		field->filter_type = filter_type; 	field->offset = offset;	field->size = size;	field->is_signed = is_signed;	field->len = len; 	list_add(&field->link, head); 	return 0;} int trace_define_field(struct trace_event_call *call, const char *type,		       const char *name, int offset, int size, int is_signed,		       int filter_type){	struct list_head *head; 	if (WARN_ON(!call->class))		return 0; 	head = trace_get_fields(call);	return __trace_define_field(head, type, name, offset, size,				    is_signed, filter_type, 0);}EXPORT_SYMBOL_GPL(trace_define_field); static int trace_define_field_ext(struct trace_event_call *call, const char *type,		       const char *name, int offset, int size, int is_signed,		       int filter_type, int len){	struct list_head *head; 	if (WARN_ON(!call->class))		return 0; 	head = trace_get_fields(call);	return __trace_define_field(head, type, name, offset, size,				    is_signed, filter_type, len);} #define __generic_field(type, item, filter_type)			\	ret = __trace_define_field(&ftrace_generic_fields, #type,	\				   #item, 0, 0, is_signed_type(type),	\				   filter_type, 0);			\	if (ret)							\		return ret; #define __common_field(type, item)					\	ret = __trace_define_field(&ftrace_common_fields, #type,	\				   "common_" #item,			\				   offsetof(typeof(ent), item),		\				   sizeof(ent.item),			\				   is_signed_type(type), FILTER_OTHER, 0);	\	if (ret)							\		return ret; static int trace_define_generic_fields(void){	int ret; 	__generic_field(int, CPU, FILTER_CPU);	__generic_field(int, cpu, FILTER_CPU);	__generic_field(int, common_cpu, FILTER_CPU);	__generic_field(char *, COMM, FILTER_COMM);	__generic_field(char *, comm, FILTER_COMM); 	return ret;} static int trace_define_common_fields(void){	int ret;	struct trace_entry ent; 	__common_field(unsigned short, type);	__common_field(unsigned char, flags);	/* Holds both preempt_count and migrate_disable */	__common_field(unsigned char, preempt_count);	__common_field(int, pid); 	return ret;} static void trace_destroy_fields(struct trace_event_call *call){	struct ftrace_event_field *field, *next;	struct list_head *head; 	head = trace_get_fields(call);	list_for_each_entry_safe(field, next, head, link) {		list_del(&field->link);		kmem_cache_free(field_cachep, field);	}} /* * run-time version of trace_event_get_offsets_<call>() that returns the last * accessible offset of trace fields excluding __dynamic_array bytes */int trace_event_get_offsets(struct trace_event_call *call){	struct ftrace_event_field *tail;	struct list_head *head; 	head = trace_get_fields(call);	/*	 * head->next points to the last field with the largest offset,	 * since it was added last by trace_define_field()	 */	tail = list_first_entry(head, struct ftrace_event_field, link);	return tail->offset + tail->size;} /* * Check if the referenced field is an array and return true, * as arrays are OK to dereference. */static bool test_field(const char *fmt, struct trace_event_call *call){	struct trace_event_fields *field = call->class->fields_array;	const char *array_descriptor;	const char *p = fmt;	int len; 	if (!(len = str_has_prefix(fmt, "REC->")))		return false;	fmt += len;	for (p = fmt; *p; p++) {		if (!isalnum(*p) && *p != '_')			break;	}	len = p - fmt; 	for (; field->type; field++) {		if (strncmp(field->name, fmt, len) ||		    field->name[len])			continue;		array_descriptor = strchr(field->type, '[');		/* This is an array and is OK to dereference. */		return array_descriptor != NULL;	}	return false;} /* * Examine the print fmt of the event looking for unsafe dereference * pointers using %p* that could be recorded in the trace event and * much later referenced after the pointer was freed. Dereferencing * pointers are OK, if it is dereferenced into the event itself. */static void test_event_printk(struct trace_event_call *call){	u64 dereference_flags = 0;	bool first = true;	const char *fmt, *c, *r, *a;	int parens = 0;	char in_quote = 0;	int start_arg = 0;	int arg = 0;	int i; 	fmt = call->print_fmt; 	if (!fmt)		return; 	for (i = 0; fmt[i]; i++) {		switch (fmt[i]) {		case '\\':			i++;			if (!fmt[i])				return;			continue;		case '"':		case '\'':			/*			 * The print fmt starts with a string that			 * is processed first to find %p* usage,			 * then after the first string, the print fmt			 * contains arguments that are used to check			 * if the dereferenced %p* usage is safe.			 */			if (first) {				if (fmt[i] == '\'')					continue;				if (in_quote) {					arg = 0;					first = false;					/*					 * If there was no %p* uses					 * the fmt is OK.					 */					if (!dereference_flags)						return;				}			}			if (in_quote) {				if (in_quote == fmt[i])					in_quote = 0;			} else {				in_quote = fmt[i];			}			continue;		case '%':			if (!first || !in_quote)				continue;			i++;			if (!fmt[i])				return;			switch (fmt[i]) {			case '%':				continue;			case 'p':				/* Find dereferencing fields */				switch (fmt[i + 1]) {				case 'B': case 'R': case 'r':				case 'b': case 'M': case 'm':				case 'I': case 'i': case 'E':				case 'U': case 'V': case 'N':				case 'a': case 'd': case 'D':				case 'g': case 't': case 'C':				case 'O': case 'f':					if (WARN_ONCE(arg == 63,						      "Too many args for event: %s",						      trace_event_name(call)))						return;					dereference_flags |= 1ULL << arg;				}				break;			default:			{				bool star = false;				int j; 				/* Increment arg if %*s exists. */				for (j = 0; fmt[i + j]; j++) {					if (isdigit(fmt[i + j]) ||					    fmt[i + j] == '.')						continue;					if (fmt[i + j] == '*') {						star = true;						continue;					}					if ((fmt[i + j] == 's') && star)						arg++;					break;				}				break;			} /* default */ 			} /* switch */			arg++;			continue;		case '(':			if (in_quote)				continue;			parens++;			continue;		case ')':			if (in_quote)				continue;			parens--;			if (WARN_ONCE(parens < 0,				      "Paren mismatch for event: %s\narg='%s'\n%*s",				      trace_event_name(call),				      fmt + start_arg,				      (i - start_arg) + 5, "^"))				return;			continue;		case ',':			if (in_quote || parens)				continue;			i++;			while (isspace(fmt[i]))				i++;			start_arg = i;			if (!(dereference_flags & (1ULL << arg)))				goto next_arg; 			/* Find the REC-> in the argument */			c = strchr(fmt + i, ',');			r = strstr(fmt + i, "REC->");			if (r && (!c || r < c)) {				/*				 * Addresses of events on the buffer,				 * or an array on the buffer is				 * OK to dereference.				 * There's ways to fool this, but				 * this is to catch common mistakes,				 * not malicious code.				 */				a = strchr(fmt + i, '&');				if ((a && (a < r)) || test_field(r, call))					dereference_flags &= ~(1ULL << arg);			} else if ((r = strstr(fmt + i, "__get_dynamic_array(")) &&				   (!c || r < c)) {				dereference_flags &= ~(1ULL << arg);			} else if ((r = strstr(fmt + i, "__get_sockaddr(")) &&				   (!c || r < c)) {				dereference_flags &= ~(1ULL << arg);			} 		next_arg:			i--;			arg++;		}	} 	/*	 * If you triggered the below warning, the trace event reported	 * uses an unsafe dereference pointer %p*. As the data stored	 * at the trace event time may no longer exist when the trace	 * event is printed, dereferencing to the original source is	 * unsafe. The source of the dereference must be copied into the	 * event itself, and the dereference must access the copy instead.	 */	if (WARN_ON_ONCE(dereference_flags)) {		arg = 1;		while (!(dereference_flags & 1)) {			dereference_flags >>= 1;			arg++;		}		pr_warn("event %s has unsafe dereference of argument %d\n",			trace_event_name(call), arg);		pr_warn("print_fmt: %s\n", fmt);	}} int trace_event_raw_init(struct trace_event_call *call){	int id; 	id = register_trace_event(&call->event);	if (!id)		return -ENODEV; 	test_event_printk(call); 	return 0;}EXPORT_SYMBOL_GPL(trace_event_raw_init); bool trace_event_ignore_this_pid(struct trace_event_file *trace_file){	struct trace_array *tr = trace_file->tr;	struct trace_array_cpu *data;	struct trace_pid_list *no_pid_list;	struct trace_pid_list *pid_list; 	pid_list = rcu_dereference_raw(tr->filtered_pids);	no_pid_list = rcu_dereference_raw(tr->filtered_no_pids); 	if (!pid_list && !no_pid_list)		return false; 	data = this_cpu_ptr(tr->array_buffer.data); 	return data->ignore_pid;}EXPORT_SYMBOL_GPL(trace_event_ignore_this_pid); void *trace_event_buffer_reserve(struct trace_event_buffer *fbuffer,				 struct trace_event_file *trace_file,				 unsigned long len){	struct trace_event_call *event_call = trace_file->event_call; 	if ((trace_file->flags & EVENT_FILE_FL_PID_FILTER) &&	    trace_event_ignore_this_pid(trace_file))

Lines 1–500 of 4160.