Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+14 −4
Original line number Diff line number Diff line
@@ -1441,12 +1441,22 @@ ftrace_hash_move(struct ftrace_ops *ops, int enable,
 * the hashes are freed with call_rcu_sched().
 */
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 *notrace_hash;
	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);
	notrace_hash = rcu_dereference_raw_notrace(ops->notrace_hash);

@@ -4218,7 +4228,7 @@ static inline void ftrace_startup_enable(int command) { }
# define ftrace_shutdown_sysctl()	do { } while (0)

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;
}
@@ -4241,7 +4251,7 @@ ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip,
	do_for_each_ftrace_op(op, ftrace_control_list) {
		if (!(op->flags & FTRACE_OPS_FL_STUB) &&
		    !ftrace_function_local_disabled(op) &&
		    ftrace_ops_test(op, ip))
		    ftrace_ops_test(op, ip, regs))
			op->func(ip, parent_ip, op, regs);
	} while_for_each_ftrace_op(op);
	trace_recursion_clear(TRACE_CONTROL_BIT);
@@ -4274,7 +4284,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
	 */
	preempt_disable_notrace();
	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);
	} while_for_each_ftrace_op(op);
	preempt_enable_notrace();