| From 794de08a16cf1fc1bf785dc48f66d36218cf6d88 Mon Sep 17 00:00:00 2001 |
| From: "Steven Rostedt (Red Hat)" <rostedt@goodmis.org> |
| Date: Thu, 8 Dec 2016 20:54:49 -0500 |
| Subject: fgraph: Handle a case where a tracer ignores set_graph_notrace |
| |
| From: Steven Rostedt (Red Hat) <rostedt@goodmis.org> |
| |
| commit 794de08a16cf1fc1bf785dc48f66d36218cf6d88 upstream. |
| |
| Both the wakeup and irqsoff tracers can use the function graph tracer when |
| the display-graph option is set. The problem is that they ignore the notrace |
| file, and record the entry of functions that would be ignored by the |
| function_graph tracer. This causes the trace->depth to be recorded into the |
| ring buffer. The set_graph_notrace uses a trick by adding a large negative |
| number to the trace->depth when a graph function is to be ignored. |
| |
| On trace output, the graph function uses the depth to record a stack of |
| functions. But since the depth is negative, it accesses the array with a |
| negative number and causes an out of bounds access that can cause a kernel |
| oops or corrupt data. |
| |
| Have the print functions handle cases where a tracer still records functions |
| even when they are in set_graph_notrace. |
| |
| Also add warnings if the depth is below zero before accessing the array. |
| |
| Note, the function graph logic will still prevent the return of these |
| functions from being recorded, which means that they will be left hanging |
| without a return. For example: |
| |
| # echo '*spin*' > set_graph_notrace |
| # echo 1 > options/display-graph |
| # echo wakeup > current_tracer |
| # cat trace |
| [...] |
| _raw_spin_lock() { |
| preempt_count_add() { |
| do_raw_spin_lock() { |
| update_rq_clock(); |
| |
| Where it should look like: |
| |
| _raw_spin_lock() { |
| preempt_count_add(); |
| do_raw_spin_lock(); |
| } |
| update_rq_clock(); |
| |
| Cc: Namhyung Kim <namhyung.kim@lge.com> |
| Fixes: 29ad23b00474 ("ftrace: Add set_graph_notrace filter") |
| Signed-off-by: Steven Rostedt <rostedt@goodmis.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| kernel/trace/trace_functions_graph.c | 17 ++++++++++++++--- |
| 1 file changed, 14 insertions(+), 3 deletions(-) |
| |
| --- a/kernel/trace/trace_functions_graph.c |
| +++ b/kernel/trace/trace_functions_graph.c |
| @@ -780,6 +780,10 @@ print_graph_entry_leaf(struct trace_iter |
| |
| cpu_data = per_cpu_ptr(data->cpu_data, cpu); |
| |
| + /* If a graph tracer ignored set_graph_notrace */ |
| + if (call->depth < -1) |
| + call->depth += FTRACE_NOTRACE_DEPTH; |
| + |
| /* |
| * Comments display at + 1 to depth. Since |
| * this is a leaf function, keep the comments |
| @@ -788,7 +792,8 @@ print_graph_entry_leaf(struct trace_iter |
| cpu_data->depth = call->depth - 1; |
| |
| /* No need to keep this function around for this depth */ |
| - if (call->depth < FTRACE_RETFUNC_DEPTH) |
| + if (call->depth < FTRACE_RETFUNC_DEPTH && |
| + !WARN_ON_ONCE(call->depth < 0)) |
| cpu_data->enter_funcs[call->depth] = 0; |
| } |
| |
| @@ -818,11 +823,16 @@ print_graph_entry_nested(struct trace_it |
| struct fgraph_cpu_data *cpu_data; |
| int cpu = iter->cpu; |
| |
| + /* If a graph tracer ignored set_graph_notrace */ |
| + if (call->depth < -1) |
| + call->depth += FTRACE_NOTRACE_DEPTH; |
| + |
| cpu_data = per_cpu_ptr(data->cpu_data, cpu); |
| cpu_data->depth = call->depth; |
| |
| /* Save this function pointer to see if the exit matches */ |
| - if (call->depth < FTRACE_RETFUNC_DEPTH) |
| + if (call->depth < FTRACE_RETFUNC_DEPTH && |
| + !WARN_ON_ONCE(call->depth < 0)) |
| cpu_data->enter_funcs[call->depth] = call->func; |
| } |
| |
| @@ -1052,7 +1062,8 @@ print_graph_return(struct ftrace_graph_r |
| */ |
| cpu_data->depth = trace->depth - 1; |
| |
| - if (trace->depth < FTRACE_RETFUNC_DEPTH) { |
| + if (trace->depth < FTRACE_RETFUNC_DEPTH && |
| + !WARN_ON_ONCE(trace->depth < 0)) { |
| if (cpu_data->enter_funcs[trace->depth] != trace->func) |
| func_match = 0; |
| cpu_data->enter_funcs[trace->depth] = 0; |