5101 lines · 119.2 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, const 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,				int need_test){	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->needs_test = need_test;	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, 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, int need_test){	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, need_test);} #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, 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, 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);	__generic_field(char *, stacktrace, FILTER_STACKTRACE);	__generic_field(char *, STACKTRACE, FILTER_STACKTRACE); 	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;}  static struct trace_event_fields *find_event_field(const char *fmt,						   struct trace_event_call *call){	struct trace_event_fields *field = call->class->fields_array;	const char *p = fmt;	int len; 	if (!(len = str_has_prefix(fmt, "REC->")))		return NULL;	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; 		return field;	}	return NULL;} /* * 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; 	field = find_event_field(fmt, call);	if (!field)		return false; 	/* This is an array and is OK to dereference. */	return strchr(field->type, '[') != NULL;} /* Look for a string within an argument */static bool find_print_string(const char *arg, const char *str, const char *end){	const char *r; 	r = strstr(arg, str);	return r && r < end;} /* Return true if the argument pointer is safe */static bool process_pointer(const char *fmt, int len, struct trace_event_call *call){	const char *r, *e, *a; 	e = fmt + len; 	/* Find the REC-> in the argument */	r = strstr(fmt, "REC->");	if (r && r < e) {		/*		 * 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, '&');		if ((a && (a < r)) || test_field(r, call))			return true;	} else if (find_print_string(fmt, "__get_dynamic_array(", e)) {		return true;	} else if (find_print_string(fmt, "__get_rel_dynamic_array(", e)) {		return true;	} else if (find_print_string(fmt, "__get_dynamic_array_len(", e)) {		return true;	} else if (find_print_string(fmt, "__get_rel_dynamic_array_len(", e)) {		return true;	} else if (find_print_string(fmt, "__get_sockaddr(", e)) {		return true;	} else if (find_print_string(fmt, "__get_rel_sockaddr(", e)) {		return true;	}	return false;} /* Return true if the string is safe */static bool process_string(const char *fmt, int len, struct trace_event_call *call){	struct trace_event_fields *field;	const char *r, *e, *s; 	e = fmt + len; 	/*	 * There are several helper functions that return strings.	 * If the argument contains a function, then assume its field is valid.	 * It is considered that the argument has a function if it has:	 *   alphanumeric or '_' before a parenthesis.	 */	s = fmt;	do {		r = strstr(s, "(");		if (!r || r >= e)			break;		for (int i = 1; r - i >= s; i++) {			char ch = *(r - i);			if (isspace(ch))				continue;			if (isalnum(ch) || ch == '_')				return true;			/* Anything else, this isn't a function */			break;		}		/* A function could be wrapped in parenthesis, try the next one */		s = r + 1;	} while (s < e); 	/*	 * Check for arrays. If the argument has: foo[REC->val]	 * then it is very likely that foo is an array of strings	 * that are safe to use.	 */	r = strstr(s, "[");	if (r && r < e) {		r = strstr(r, "REC->");		if (r && r < e)			return true;	} 	/*	 * If there's any strings in the argument consider this arg OK as it	 * could be: REC->field ? "foo" : "bar" and we don't want to get into	 * verifying that logic here.	 */	if (find_print_string(fmt, "\"", e))		return true; 	/* Dereferenced strings are also valid like any other pointer */	if (process_pointer(fmt, len, call))		return true; 	/* Make sure the field is found */	field = find_event_field(fmt, call);	if (!field)		return false; 	/* Test this field's string before printing the event */	call->flags |= TRACE_EVENT_FL_TEST_STR;	field->needs_test = 1; 	return true;} static void handle_dereference_arg(const char *arg_str, u64 string_flags, int len,				   u64 *dereference_flags, int arg,				   struct trace_event_call *call){	if (string_flags & (1ULL << arg)) {		if (process_string(arg_str, len, call))			*dereference_flags &= ~(1ULL << arg);	} else if (process_pointer(arg_str, len, call))		*dereference_flags &= ~(1ULL << arg);	else		pr_warn("TRACE EVENT ERROR: Bad dereference argument: '%.*s'\n",			len, arg_str);} /* * 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;	u64 string_flags = 0;	bool first = true;	const char *fmt;	int parens = 0;	char in_quote = 0;	int start_arg = 0;	int arg = 0;	int i, e; 	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': do_pointer:				/* 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;

Lines 1–500 of 5101.