Commit 195a8afc authored by Steven Rostedt (Red Hat)'s avatar Steven Rostedt (Red Hat) Committed by Steven Rostedt

ftrace: Add check for NULL regs if ops has SAVE_REGS set

If a ftrace ops is registered with the SAVE_REGS flag set, and there's
already a ops registered to one of its functions but without the
SAVE_REGS flag, there's a small race window where the SAVE_REGS ops gets
added to the list of callbacks to call for that function before the
callback trampoline gets set to save the regs.

The problem is, the function is not currently saving regs, which opens
a small race window where the ops that is expecting regs to be passed
to it, wont. This can cause a crash if the callback were to reference
the regs, as the SAVE_REGS guarantees that regs will be set.

To fix this, we add a check in the loop case where it checks if the ops
has the SAVE_REGS flag set, and if so, it will ignore it if regs is
not set.
Signed-off-by: default avatarSteven Rostedt <rostedt@goodmis.org>
parent 9c01fe45
...@@ -1441,12 +1441,22 @@ ftrace_hash_move(struct ftrace_ops *ops, int enable, ...@@ -1441,12 +1441,22 @@ ftrace_hash_move(struct ftrace_ops *ops, int enable,
* the hashes are freed with call_rcu_sched(). * the hashes are freed with call_rcu_sched().
*/ */
static int static int
ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs)
{ {
struct ftrace_hash *filter_hash; struct ftrace_hash *filter_hash;
struct ftrace_hash *notrace_hash; struct ftrace_hash *notrace_hash;
int ret; int ret;
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
/*
* There's a small race when adding ops that the ftrace handler
* that wants regs, may be called without them. We can not
* allow that handler to be called if regs is NULL.
*/
if (regs == NULL && (ops->flags & FTRACE_OPS_FL_SAVE_REGS))
return 0;
#endif
filter_hash = rcu_dereference_raw_notrace(ops->filter_hash); filter_hash = rcu_dereference_raw_notrace(ops->filter_hash);
notrace_hash = rcu_dereference_raw_notrace(ops->notrace_hash); notrace_hash = rcu_dereference_raw_notrace(ops->notrace_hash);
...@@ -4218,7 +4228,7 @@ static inline void ftrace_startup_enable(int command) { } ...@@ -4218,7 +4228,7 @@ static inline void ftrace_startup_enable(int command) { }
# define ftrace_shutdown_sysctl() do { } while (0) # define ftrace_shutdown_sysctl() do { } while (0)
static inline int static inline int
ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs)
{ {
return 1; return 1;
} }
...@@ -4241,7 +4251,7 @@ ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip, ...@@ -4241,7 +4251,7 @@ ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip,
do_for_each_ftrace_op(op, ftrace_control_list) { do_for_each_ftrace_op(op, ftrace_control_list) {
if (!(op->flags & FTRACE_OPS_FL_STUB) && if (!(op->flags & FTRACE_OPS_FL_STUB) &&
!ftrace_function_local_disabled(op) && !ftrace_function_local_disabled(op) &&
ftrace_ops_test(op, ip)) ftrace_ops_test(op, ip, regs))
op->func(ip, parent_ip, op, regs); op->func(ip, parent_ip, op, regs);
} while_for_each_ftrace_op(op); } while_for_each_ftrace_op(op);
trace_recursion_clear(TRACE_CONTROL_BIT); trace_recursion_clear(TRACE_CONTROL_BIT);
...@@ -4274,7 +4284,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip, ...@@ -4274,7 +4284,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
*/ */
preempt_disable_notrace(); preempt_disable_notrace();
do_for_each_ftrace_op(op, ftrace_ops_list) { do_for_each_ftrace_op(op, ftrace_ops_list) {
if (ftrace_ops_test(op, ip)) if (ftrace_ops_test(op, ip, regs))
op->func(ip, parent_ip, op, regs); op->func(ip, parent_ip, op, regs);
} while_for_each_ftrace_op(op); } while_for_each_ftrace_op(op);
preempt_enable_notrace(); preempt_enable_notrace();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment