mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	ftrace: Add selftest to test function trace recursion protection
Add selftests to test the function tracing recursion protection actually does work. It also tests if a ftrace_ops states it will perform its own protection. Although, even if the ftrace_ops states it will protect itself, the ftrace infrastructure may still provide protection if the arch does not support all features or another ftrace_ops is registered. Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
This commit is contained in:
		
							parent
							
								
									47239c4d8d
								
							
						
					
					
						commit
						ea701f11da
					
				
					 3 changed files with 163 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -220,6 +220,10 @@ extern void ftrace_stub(unsigned long a0, unsigned long a1,
 | 
			
		|||
 */
 | 
			
		||||
#define register_ftrace_function(ops) ({ 0; })
 | 
			
		||||
#define unregister_ftrace_function(ops) ({ 0; })
 | 
			
		||||
static inline int ftrace_nr_registered_ops(void)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
static inline void clear_ftrace_function(void) { }
 | 
			
		||||
static inline void ftrace_kill(void) { }
 | 
			
		||||
static inline void ftrace_stop(void) { }
 | 
			
		||||
| 
						 | 
				
			
			@ -275,6 +279,8 @@ extern void unregister_ftrace_function_probe_all(char *glob);
 | 
			
		|||
 | 
			
		||||
extern int ftrace_text_reserved(void *start, void *end);
 | 
			
		||||
 | 
			
		||||
extern int ftrace_nr_registered_ops(void);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The dyn_ftrace record's flags field is split into two parts.
 | 
			
		||||
 * the first part which is '0-FTRACE_REF_MAX' is a counter of
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -111,6 +111,27 @@ static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip);
 | 
			
		|||
#define ftrace_ops_list_func ((ftrace_func_t)ftrace_ops_no_ops)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ftrace_nr_registered_ops - return number of ops registered
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the number of ftrace_ops registered and tracing functions
 | 
			
		||||
 */
 | 
			
		||||
int ftrace_nr_registered_ops(void)
 | 
			
		||||
{
 | 
			
		||||
	struct ftrace_ops *ops;
 | 
			
		||||
	int cnt = 0;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&ftrace_lock);
 | 
			
		||||
 | 
			
		||||
	for (ops = ftrace_ops_list;
 | 
			
		||||
	     ops != &ftrace_list_end; ops = ops->next)
 | 
			
		||||
		cnt++;
 | 
			
		||||
 | 
			
		||||
	mutex_unlock(&ftrace_lock);
 | 
			
		||||
 | 
			
		||||
	return cnt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Traverse the ftrace_global_list, invoking all entries.  The reason that we
 | 
			
		||||
 * can use rcu_dereference_raw() is that elements removed from this list
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -406,8 +406,141 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
 | 
			
		|||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int trace_selftest_recursion_cnt;
 | 
			
		||||
static void trace_selftest_test_recursion_func(unsigned long ip,
 | 
			
		||||
					       unsigned long pip,
 | 
			
		||||
					       struct ftrace_ops *op,
 | 
			
		||||
					       struct pt_regs *pt_regs)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * This function is registered without the recursion safe flag.
 | 
			
		||||
	 * The ftrace infrastructure should provide the recursion
 | 
			
		||||
	 * protection. If not, this will crash the kernel!
 | 
			
		||||
	 */
 | 
			
		||||
	trace_selftest_recursion_cnt++;
 | 
			
		||||
	DYN_FTRACE_TEST_NAME();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void trace_selftest_test_recursion_safe_func(unsigned long ip,
 | 
			
		||||
						    unsigned long pip,
 | 
			
		||||
						    struct ftrace_ops *op,
 | 
			
		||||
						    struct pt_regs *pt_regs)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * We said we would provide our own recursion. By calling
 | 
			
		||||
	 * this function again, we should recurse back into this function
 | 
			
		||||
	 * and count again. But this only happens if the arch supports
 | 
			
		||||
	 * all of ftrace features and nothing else is using the function
 | 
			
		||||
	 * tracing utility.
 | 
			
		||||
	 */
 | 
			
		||||
	if (trace_selftest_recursion_cnt++)
 | 
			
		||||
		return;
 | 
			
		||||
	DYN_FTRACE_TEST_NAME();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ftrace_ops test_rec_probe = {
 | 
			
		||||
	.func			= trace_selftest_test_recursion_func,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct ftrace_ops test_recsafe_probe = {
 | 
			
		||||
	.func			= trace_selftest_test_recursion_safe_func,
 | 
			
		||||
	.flags			= FTRACE_OPS_FL_RECURSION_SAFE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
trace_selftest_function_recursion(void)
 | 
			
		||||
{
 | 
			
		||||
	int save_ftrace_enabled = ftrace_enabled;
 | 
			
		||||
	int save_tracer_enabled = tracer_enabled;
 | 
			
		||||
	char *func_name;
 | 
			
		||||
	int len;
 | 
			
		||||
	int ret;
 | 
			
		||||
	int cnt;
 | 
			
		||||
 | 
			
		||||
	/* The previous test PASSED */
 | 
			
		||||
	pr_cont("PASSED\n");
 | 
			
		||||
	pr_info("Testing ftrace recursion: ");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/* enable tracing, and record the filter function */
 | 
			
		||||
	ftrace_enabled = 1;
 | 
			
		||||
	tracer_enabled = 1;
 | 
			
		||||
 | 
			
		||||
	/* Handle PPC64 '.' name */
 | 
			
		||||
	func_name = "*" __stringify(DYN_FTRACE_TEST_NAME);
 | 
			
		||||
	len = strlen(func_name);
 | 
			
		||||
 | 
			
		||||
	ret = ftrace_set_filter(&test_rec_probe, func_name, len, 1);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_cont("*Could not set filter* ");
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = register_ftrace_function(&test_rec_probe);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_cont("*could not register callback* ");
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DYN_FTRACE_TEST_NAME();
 | 
			
		||||
 | 
			
		||||
	unregister_ftrace_function(&test_rec_probe);
 | 
			
		||||
 | 
			
		||||
	ret = -1;
 | 
			
		||||
	if (trace_selftest_recursion_cnt != 1) {
 | 
			
		||||
		pr_cont("*callback not called once (%d)* ",
 | 
			
		||||
			trace_selftest_recursion_cnt);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	trace_selftest_recursion_cnt = 1;
 | 
			
		||||
 | 
			
		||||
	pr_cont("PASSED\n");
 | 
			
		||||
	pr_info("Testing ftrace recursion safe: ");
 | 
			
		||||
 | 
			
		||||
	ret = ftrace_set_filter(&test_recsafe_probe, func_name, len, 1);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_cont("*Could not set filter* ");
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = register_ftrace_function(&test_recsafe_probe);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_cont("*could not register callback* ");
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DYN_FTRACE_TEST_NAME();
 | 
			
		||||
 | 
			
		||||
	unregister_ftrace_function(&test_recsafe_probe);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If arch supports all ftrace features, and no other task
 | 
			
		||||
	 * was on the list, we should be fine.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!ftrace_nr_registered_ops() && !FTRACE_FORCE_LIST_FUNC)
 | 
			
		||||
		cnt = 2; /* Should have recursed */
 | 
			
		||||
	else
 | 
			
		||||
		cnt = 1;
 | 
			
		||||
 | 
			
		||||
	ret = -1;
 | 
			
		||||
	if (trace_selftest_recursion_cnt != cnt) {
 | 
			
		||||
		pr_cont("*callback not called expected %d times (%d)* ",
 | 
			
		||||
			cnt, trace_selftest_recursion_cnt);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = 0;
 | 
			
		||||
out:
 | 
			
		||||
	ftrace_enabled = save_ftrace_enabled;
 | 
			
		||||
	tracer_enabled = save_tracer_enabled;
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
# define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; })
 | 
			
		||||
# define trace_selftest_function_recursion() ({ 0; })
 | 
			
		||||
#endif /* CONFIG_DYNAMIC_FTRACE */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -455,7 +588,10 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
 | 
			
		|||
 | 
			
		||||
	ret = trace_selftest_startup_dynamic_tracing(trace, tr,
 | 
			
		||||
						     DYN_FTRACE_TEST_NAME);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	ret = trace_selftest_function_recursion();
 | 
			
		||||
 out:
 | 
			
		||||
	ftrace_enabled = save_ftrace_enabled;
 | 
			
		||||
	tracer_enabled = save_tracer_enabled;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue