Commit 794de08a authored by Steven Rostedt (Red Hat)'s avatar Steven Rostedt (Red Hat) Committed by Steven Rostedt

fgraph: Handle a case where a tracer ignores set_graph_notrace

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: stable@vger.kernel.org
Cc: Namhyung Kim <namhyung.kim@lge.com>
Fixes: 29ad23b0 ("ftrace: Add set_graph_notrace filter")
Signed-off-by: default avatarSteven Rostedt <rostedt@goodmis.org>
parent 656c7f0d
...@@ -842,6 +842,10 @@ print_graph_entry_leaf(struct trace_iterator *iter, ...@@ -842,6 +842,10 @@ print_graph_entry_leaf(struct trace_iterator *iter,
cpu_data = per_cpu_ptr(data->cpu_data, cpu); 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 * Comments display at + 1 to depth. Since
* this is a leaf function, keep the comments * this is a leaf function, keep the comments
...@@ -850,7 +854,8 @@ print_graph_entry_leaf(struct trace_iterator *iter, ...@@ -850,7 +854,8 @@ print_graph_entry_leaf(struct trace_iterator *iter,
cpu_data->depth = call->depth - 1; cpu_data->depth = call->depth - 1;
/* No need to keep this function around for this depth */ /* 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; cpu_data->enter_funcs[call->depth] = 0;
} }
...@@ -880,11 +885,16 @@ print_graph_entry_nested(struct trace_iterator *iter, ...@@ -880,11 +885,16 @@ print_graph_entry_nested(struct trace_iterator *iter,
struct fgraph_cpu_data *cpu_data; struct fgraph_cpu_data *cpu_data;
int cpu = iter->cpu; 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 = per_cpu_ptr(data->cpu_data, cpu);
cpu_data->depth = call->depth; cpu_data->depth = call->depth;
/* Save this function pointer to see if the exit matches */ /* 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; cpu_data->enter_funcs[call->depth] = call->func;
} }
...@@ -1114,7 +1124,8 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, ...@@ -1114,7 +1124,8 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
*/ */
cpu_data->depth = trace->depth - 1; 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) if (cpu_data->enter_funcs[trace->depth] != trace->func)
func_match = 0; func_match = 0;
cpu_data->enter_funcs[trace->depth] = 0; cpu_data->enter_funcs[trace->depth] = 0;
......
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