aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/ftrace.c
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2013-07-23 22:06:15 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-08-29 12:47:32 -0400
commit29632b10ef02a31dec2918177f6d4244e66d635d (patch)
tree5890cb1d61bfda1845241d6891516ef86041c79c /kernel/trace/ftrace.c
parent7272711a2fc64dd6ef980630e129d8731897ee56 (diff)
ftrace: Add check for NULL regs if ops has SAVE_REGS set
commit 195a8afc7ac962f8da795549fe38e825f1372b0d upstream. 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: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r--kernel/trace/ftrace.c18
1 files changed, 14 insertions, 4 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 6c508ff33c62..13f12351b350 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1416,12 +1416,22 @@ ftrace_hash_move(struct ftrace_ops *ops, int enable,
1416 * the hashes are freed with call_rcu_sched(). 1416 * the hashes are freed with call_rcu_sched().
1417 */ 1417 */
1418static int 1418static int
1419ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) 1419ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs)
1420{ 1420{
1421 struct ftrace_hash *filter_hash; 1421 struct ftrace_hash *filter_hash;
1422 struct ftrace_hash *notrace_hash; 1422 struct ftrace_hash *notrace_hash;
1423 int ret; 1423 int ret;
1424 1424
1425#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
1426 /*
1427 * There's a small race when adding ops that the ftrace handler
1428 * that wants regs, may be called without them. We can not
1429 * allow that handler to be called if regs is NULL.
1430 */
1431 if (regs == NULL && (ops->flags & FTRACE_OPS_FL_SAVE_REGS))
1432 return 0;
1433#endif
1434
1425 filter_hash = rcu_dereference_raw_notrace(ops->filter_hash); 1435 filter_hash = rcu_dereference_raw_notrace(ops->filter_hash);
1426 notrace_hash = rcu_dereference_raw_notrace(ops->notrace_hash); 1436 notrace_hash = rcu_dereference_raw_notrace(ops->notrace_hash);
1427 1437
@@ -4188,7 +4198,7 @@ static inline void ftrace_startup_enable(int command) { }
4188# define ftrace_shutdown_sysctl() do { } while (0) 4198# define ftrace_shutdown_sysctl() do { } while (0)
4189 4199
4190static inline int 4200static inline int
4191ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) 4201ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs)
4192{ 4202{
4193 return 1; 4203 return 1;
4194} 4204}
@@ -4211,7 +4221,7 @@ ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip,
4211 do_for_each_ftrace_op(op, ftrace_control_list) { 4221 do_for_each_ftrace_op(op, ftrace_control_list) {
4212 if (!(op->flags & FTRACE_OPS_FL_STUB) && 4222 if (!(op->flags & FTRACE_OPS_FL_STUB) &&
4213 !ftrace_function_local_disabled(op) && 4223 !ftrace_function_local_disabled(op) &&
4214 ftrace_ops_test(op, ip)) 4224 ftrace_ops_test(op, ip, regs))
4215 op->func(ip, parent_ip, op, regs); 4225 op->func(ip, parent_ip, op, regs);
4216 } while_for_each_ftrace_op(op); 4226 } while_for_each_ftrace_op(op);
4217 trace_recursion_clear(TRACE_CONTROL_BIT); 4227 trace_recursion_clear(TRACE_CONTROL_BIT);
@@ -4244,7 +4254,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
4244 */ 4254 */
4245 preempt_disable_notrace(); 4255 preempt_disable_notrace();
4246 do_for_each_ftrace_op(op, ftrace_ops_list) { 4256 do_for_each_ftrace_op(op, ftrace_ops_list) {
4247 if (ftrace_ops_test(op, ip)) 4257 if (ftrace_ops_test(op, ip, regs))
4248 op->func(ip, parent_ip, op, regs); 4258 op->func(ip, parent_ip, op, regs);
4249 } while_for_each_ftrace_op(op); 4259 } while_for_each_ftrace_op(op);
4250 preempt_enable_notrace(); 4260 preempt_enable_notrace();