tracing/filters: add filter_mutex to protect filter predicates
This patch adds a filter_mutex to prevent the filter predicates from
being accessed concurrently by various external functions.
It's based on a previous patch by Li Zefan:
        "[PATCH 7/7] tracing/filters: make filter preds RCU safe"
v2 changes:
- fixed wrong value returned in a add_subsystem_pred() failure case
  noticed by Li Zefan.
[ Impact: fix trace filter corruption/crashes on parallel access ]
Signed-off-by: Tom Zanussi <tzanussi@gmail.com>
Reviewed-by: Li Zefan <lizf@cn.fujitsu.com>
Tested-by: Li Zefan <lizf@cn.fujitsu.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: paulmck@linux.vnet.ibm.com
LKML-Reference: <1239946028.6639.13.camel@tropicana>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
	
	
This commit is contained in:
		
					parent
					
						
							
								46de405f25
							
						
					
				
			
			
				commit
				
					
						ac1adc55fc
					
				
			
		
					 3 changed files with 75 additions and 23 deletions
				
			
		| 
						 | 
					@ -757,13 +757,15 @@ struct filter_pred {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern void filter_free_pred(struct filter_pred *pred);
 | 
					extern void filter_free_pred(struct filter_pred *pred);
 | 
				
			||||||
extern void filter_print_preds(struct filter_pred **preds, int n_preds,
 | 
					extern void filter_print_preds(struct ftrace_event_call *call,
 | 
				
			||||||
			       struct trace_seq *s);
 | 
								       struct trace_seq *s);
 | 
				
			||||||
extern int filter_parse(char **pbuf, struct filter_pred *pred);
 | 
					extern int filter_parse(char **pbuf, struct filter_pred *pred);
 | 
				
			||||||
extern int filter_add_pred(struct ftrace_event_call *call,
 | 
					extern int filter_add_pred(struct ftrace_event_call *call,
 | 
				
			||||||
			   struct filter_pred *pred);
 | 
								   struct filter_pred *pred);
 | 
				
			||||||
extern void filter_disable_preds(struct ftrace_event_call *call);
 | 
					extern void filter_disable_preds(struct ftrace_event_call *call);
 | 
				
			||||||
extern void filter_free_subsystem_preds(struct event_subsystem *system);
 | 
					extern void filter_free_subsystem_preds(struct event_subsystem *system);
 | 
				
			||||||
 | 
					extern void filter_print_subsystem_preds(struct event_subsystem *system,
 | 
				
			||||||
 | 
										 struct trace_seq *s);
 | 
				
			||||||
extern int filter_add_subsystem_pred(struct event_subsystem *system,
 | 
					extern int filter_add_subsystem_pred(struct event_subsystem *system,
 | 
				
			||||||
				     struct filter_pred *pred);
 | 
									     struct filter_pred *pred);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -488,7 +488,7 @@ event_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trace_seq_init(s);
 | 
						trace_seq_init(s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	filter_print_preds(call->preds, call->n_preds, s);
 | 
						filter_print_preds(call, s);
 | 
				
			||||||
	r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
 | 
						r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kfree(s);
 | 
						kfree(s);
 | 
				
			||||||
| 
						 | 
					@ -558,7 +558,7 @@ subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trace_seq_init(s);
 | 
						trace_seq_init(s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	filter_print_preds(system->preds, system->n_preds, s);
 | 
						filter_print_subsystem_preds(system, s);
 | 
				
			||||||
	r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
 | 
						r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kfree(s);
 | 
						kfree(s);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,10 +22,13 @@
 | 
				
			||||||
#include <linux/uaccess.h>
 | 
					#include <linux/uaccess.h>
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
#include <linux/ctype.h>
 | 
					#include <linux/ctype.h>
 | 
				
			||||||
 | 
					#include <linux/mutex.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "trace.h"
 | 
					#include "trace.h"
 | 
				
			||||||
#include "trace_output.h"
 | 
					#include "trace_output.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static DEFINE_MUTEX(filter_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int filter_pred_64(struct filter_pred *pred, void *event)
 | 
					static int filter_pred_64(struct filter_pred *pred, void *event)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	u64 *addr = (u64 *)(event + pred->offset);
 | 
						u64 *addr = (u64 *)(event + pred->offset);
 | 
				
			||||||
| 
						 | 
					@ -112,8 +115,8 @@ int filter_match_preds(struct ftrace_event_call *call, void *rec)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(filter_match_preds);
 | 
					EXPORT_SYMBOL_GPL(filter_match_preds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void filter_print_preds(struct filter_pred **preds, int n_preds,
 | 
					static void __filter_print_preds(struct filter_pred **preds, int n_preds,
 | 
				
			||||||
			struct trace_seq *s)
 | 
									 struct trace_seq *s)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char *field_name;
 | 
						char *field_name;
 | 
				
			||||||
	struct filter_pred *pred;
 | 
						struct filter_pred *pred;
 | 
				
			||||||
| 
						 | 
					@ -138,6 +141,21 @@ void filter_print_preds(struct filter_pred **preds, int n_preds,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void filter_print_preds(struct ftrace_event_call *call, struct trace_seq *s)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mutex_lock(&filter_mutex);
 | 
				
			||||||
 | 
						__filter_print_preds(call->preds, call->n_preds, s);
 | 
				
			||||||
 | 
						mutex_unlock(&filter_mutex);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void filter_print_subsystem_preds(struct event_subsystem *system,
 | 
				
			||||||
 | 
									  struct trace_seq *s)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mutex_lock(&filter_mutex);
 | 
				
			||||||
 | 
						__filter_print_preds(system->preds, system->n_preds, s);
 | 
				
			||||||
 | 
						mutex_unlock(&filter_mutex);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct ftrace_event_field *
 | 
					static struct ftrace_event_field *
 | 
				
			||||||
find_event_field(struct ftrace_event_call *call, char *name)
 | 
					find_event_field(struct ftrace_event_call *call, char *name)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -180,7 +198,7 @@ static int filter_set_pred(struct filter_pred *dest,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void filter_disable_preds(struct ftrace_event_call *call)
 | 
					static void __filter_disable_preds(struct ftrace_event_call *call)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i;
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -190,6 +208,13 @@ void filter_disable_preds(struct ftrace_event_call *call)
 | 
				
			||||||
		call->preds[i]->fn = filter_pred_none;
 | 
							call->preds[i]->fn = filter_pred_none;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void filter_disable_preds(struct ftrace_event_call *call)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mutex_lock(&filter_mutex);
 | 
				
			||||||
 | 
						__filter_disable_preds(call);
 | 
				
			||||||
 | 
						mutex_unlock(&filter_mutex);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int init_preds(struct ftrace_event_call *call)
 | 
					int init_preds(struct ftrace_event_call *call)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct filter_pred *pred;
 | 
						struct filter_pred *pred;
 | 
				
			||||||
| 
						 | 
					@ -223,7 +248,7 @@ oom:
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(init_preds);
 | 
					EXPORT_SYMBOL_GPL(init_preds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void filter_free_subsystem_preds(struct event_subsystem *system)
 | 
					static void __filter_free_subsystem_preds(struct event_subsystem *system)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ftrace_event_call *call;
 | 
						struct ftrace_event_call *call;
 | 
				
			||||||
	int i;
 | 
						int i;
 | 
				
			||||||
| 
						 | 
					@ -241,18 +266,25 @@ void filter_free_subsystem_preds(struct event_subsystem *system)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!strcmp(call->system, system->name))
 | 
							if (!strcmp(call->system, system->name))
 | 
				
			||||||
			filter_disable_preds(call);
 | 
								__filter_disable_preds(call);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __filter_add_pred(struct ftrace_event_call *call,
 | 
					void filter_free_subsystem_preds(struct event_subsystem *system)
 | 
				
			||||||
			     struct filter_pred *pred,
 | 
					{
 | 
				
			||||||
			     filter_pred_fn_t fn)
 | 
						mutex_lock(&filter_mutex);
 | 
				
			||||||
 | 
						__filter_free_subsystem_preds(system);
 | 
				
			||||||
 | 
						mutex_unlock(&filter_mutex);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int filter_add_pred_fn(struct ftrace_event_call *call,
 | 
				
			||||||
 | 
								      struct filter_pred *pred,
 | 
				
			||||||
 | 
								      filter_pred_fn_t fn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int idx, err;
 | 
						int idx, err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (call->n_preds && !pred->compound)
 | 
						if (call->n_preds && !pred->compound)
 | 
				
			||||||
		filter_disable_preds(call);
 | 
							__filter_disable_preds(call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (call->n_preds == MAX_FILTER_PRED)
 | 
						if (call->n_preds == MAX_FILTER_PRED)
 | 
				
			||||||
		return -ENOSPC;
 | 
							return -ENOSPC;
 | 
				
			||||||
| 
						 | 
					@ -276,7 +308,8 @@ static int is_string_field(const char *type)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred)
 | 
					static int __filter_add_pred(struct ftrace_event_call *call,
 | 
				
			||||||
 | 
								     struct filter_pred *pred)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ftrace_event_field *field;
 | 
						struct ftrace_event_field *field;
 | 
				
			||||||
	filter_pred_fn_t fn;
 | 
						filter_pred_fn_t fn;
 | 
				
			||||||
| 
						 | 
					@ -293,7 +326,7 @@ int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred)
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
		fn = filter_pred_string;
 | 
							fn = filter_pred_string;
 | 
				
			||||||
		pred->str_len = field->size;
 | 
							pred->str_len = field->size;
 | 
				
			||||||
		return __filter_add_pred(call, pred, fn);
 | 
							return filter_add_pred_fn(call, pred, fn);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if (pred->str_len)
 | 
							if (pred->str_len)
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
| 
						 | 
					@ -316,7 +349,18 @@ int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return __filter_add_pred(call, pred, fn);
 | 
						return filter_add_pred_fn(call, pred, fn);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&filter_mutex);
 | 
				
			||||||
 | 
						err = __filter_add_pred(call, pred);
 | 
				
			||||||
 | 
						mutex_unlock(&filter_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int filter_add_subsystem_pred(struct event_subsystem *system,
 | 
					int filter_add_subsystem_pred(struct event_subsystem *system,
 | 
				
			||||||
| 
						 | 
					@ -324,20 +368,27 @@ int filter_add_subsystem_pred(struct event_subsystem *system,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ftrace_event_call *call;
 | 
						struct ftrace_event_call *call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&filter_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (system->n_preds && !pred->compound)
 | 
						if (system->n_preds && !pred->compound)
 | 
				
			||||||
		filter_free_subsystem_preds(system);
 | 
							__filter_free_subsystem_preds(system);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!system->n_preds) {
 | 
						if (!system->n_preds) {
 | 
				
			||||||
		system->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred),
 | 
							system->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred),
 | 
				
			||||||
					GFP_KERNEL);
 | 
										GFP_KERNEL);
 | 
				
			||||||
		if (!system->preds)
 | 
							if (!system->preds) {
 | 
				
			||||||
 | 
								mutex_unlock(&filter_mutex);
 | 
				
			||||||
			return -ENOMEM;
 | 
								return -ENOMEM;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (system->n_preds == MAX_FILTER_PRED)
 | 
						if (system->n_preds == MAX_FILTER_PRED) {
 | 
				
			||||||
 | 
							mutex_unlock(&filter_mutex);
 | 
				
			||||||
		return -ENOSPC;
 | 
							return -ENOSPC;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	system->preds[system->n_preds] = pred;
 | 
						system->preds[system->n_preds] = pred;
 | 
				
			||||||
 | 
						system->n_preds++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(call, &ftrace_events, list) {
 | 
						list_for_each_entry(call, &ftrace_events, list) {
 | 
				
			||||||
		int err;
 | 
							int err;
 | 
				
			||||||
| 
						 | 
					@ -348,17 +399,16 @@ int filter_add_subsystem_pred(struct event_subsystem *system,
 | 
				
			||||||
		if (strcmp(call->system, system->name))
 | 
							if (strcmp(call->system, system->name))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!find_event_field(call, pred->field_name))
 | 
							err = __filter_add_pred(call, pred);
 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		err = filter_add_pred(call, pred);
 | 
					 | 
				
			||||||
		if (err == -ENOMEM) {
 | 
							if (err == -ENOMEM) {
 | 
				
			||||||
			system->preds[system->n_preds] = NULL;
 | 
								system->preds[system->n_preds] = NULL;
 | 
				
			||||||
 | 
								system->n_preds--;
 | 
				
			||||||
 | 
								mutex_unlock(&filter_mutex);
 | 
				
			||||||
			return err;
 | 
								return err;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	system->n_preds++;
 | 
						mutex_unlock(&filter_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue