Skip to content

Commit

Permalink
Merge tag 'trace-v5.15-rc5' of git://git.kernel.org/pub/scm/linux/ker…
Browse files Browse the repository at this point in the history
…nel/git/rostedt/linux-trace

Pull tracing fix from Steven Rostedt:
 "Recursion fix for tracing.

  While cleaning up some of the tracing recursion protection logic, I
  discovered a scenario that the current design would miss, and would
  allow an infinite recursion. Removing an optimization trick that
  opened the hole fixes the issue and cleans up the code as well"

* tag 'trace-v5.15-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace:
  tracing: Have all levels of checks prevent recursion
  • Loading branch information
torvalds committed Oct 20, 2021
2 parents 1e59977 + ed65df6 commit fc9b289
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 42 deletions.
49 changes: 9 additions & 40 deletions include/linux/trace_recursion.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,23 @@
* When function tracing occurs, the following steps are made:
* If arch does not support a ftrace feature:
* call internal function (uses INTERNAL bits) which calls...
* If callback is registered to the "global" list, the list
* function is called and recursion checks the GLOBAL bits.
* then this function calls...
* The function callback, which can use the FTRACE bits to
* check for recursion.
*
* Now if the arch does not support a feature, and it calls
* the global list function which calls the ftrace callback
* all three of these steps will do a recursion protection.
* There's no reason to do one if the previous caller already
* did. The recursion that we are protecting against will
* go through the same steps again.
*
* To prevent the multiple recursion checks, if a recursion
* bit is set that is higher than the MAX bit of the current
* check, then we know that the check was made by the previous
* caller, and we can skip the current check.
*/
enum {
/* Function recursion bits */
TRACE_FTRACE_BIT,
TRACE_FTRACE_NMI_BIT,
TRACE_FTRACE_IRQ_BIT,
TRACE_FTRACE_SIRQ_BIT,
TRACE_FTRACE_TRANSITION_BIT,

/* INTERNAL_BITs must be greater than FTRACE_BITs */
/* Internal use recursion bits */
TRACE_INTERNAL_BIT,
TRACE_INTERNAL_NMI_BIT,
TRACE_INTERNAL_IRQ_BIT,
TRACE_INTERNAL_SIRQ_BIT,
TRACE_INTERNAL_TRANSITION_BIT,

TRACE_BRANCH_BIT,
/*
Expand Down Expand Up @@ -86,12 +73,6 @@ enum {
*/
TRACE_GRAPH_NOTRACE_BIT,

/*
* When transitioning between context, the preempt_count() may
* not be correct. Allow for a single recursion to cover this case.
*/
TRACE_TRANSITION_BIT,

/* Used to prevent recursion recording from recursing. */
TRACE_RECORD_RECURSION_BIT,
};
Expand All @@ -113,12 +94,10 @@ enum {
#define TRACE_CONTEXT_BITS 4

#define TRACE_FTRACE_START TRACE_FTRACE_BIT
#define TRACE_FTRACE_MAX ((1 << (TRACE_FTRACE_START + TRACE_CONTEXT_BITS)) - 1)

#define TRACE_LIST_START TRACE_INTERNAL_BIT
#define TRACE_LIST_MAX ((1 << (TRACE_LIST_START + TRACE_CONTEXT_BITS)) - 1)

#define TRACE_CONTEXT_MASK TRACE_LIST_MAX
#define TRACE_CONTEXT_MASK ((1 << (TRACE_LIST_START + TRACE_CONTEXT_BITS)) - 1)

/*
* Used for setting context
Expand All @@ -132,6 +111,7 @@ enum {
TRACE_CTX_IRQ,
TRACE_CTX_SOFTIRQ,
TRACE_CTX_NORMAL,
TRACE_CTX_TRANSITION,
};

static __always_inline int trace_get_context_bit(void)
Expand Down Expand Up @@ -160,45 +140,34 @@ extern void ftrace_record_recursion(unsigned long ip, unsigned long parent_ip);
#endif

static __always_inline int trace_test_and_set_recursion(unsigned long ip, unsigned long pip,
int start, int max)
int start)
{
unsigned int val = READ_ONCE(current->trace_recursion);
int bit;

/* A previous recursion check was made */
if ((val & TRACE_CONTEXT_MASK) > max)
return 0;

bit = trace_get_context_bit() + start;
if (unlikely(val & (1 << bit))) {
/*
* It could be that preempt_count has not been updated during
* a switch between contexts. Allow for a single recursion.
*/
bit = TRACE_TRANSITION_BIT;
bit = TRACE_CTX_TRANSITION + start;
if (val & (1 << bit)) {
do_ftrace_record_recursion(ip, pip);
return -1;
}
} else {
/* Normal check passed, clear the transition to allow it again */
val &= ~(1 << TRACE_TRANSITION_BIT);
}

val |= 1 << bit;
current->trace_recursion = val;
barrier();

return bit + 1;
return bit;
}

static __always_inline void trace_clear_recursion(int bit)
{
if (!bit)
return;

barrier();
bit--;
trace_recursion_clear(bit);
}

Expand All @@ -214,7 +183,7 @@ static __always_inline void trace_clear_recursion(int bit)
static __always_inline int ftrace_test_recursion_trylock(unsigned long ip,
unsigned long parent_ip)
{
return trace_test_and_set_recursion(ip, parent_ip, TRACE_FTRACE_START, TRACE_FTRACE_MAX);
return trace_test_and_set_recursion(ip, parent_ip, TRACE_FTRACE_START);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions kernel/trace/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -6977,7 +6977,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op;
int bit;

bit = trace_test_and_set_recursion(ip, parent_ip, TRACE_LIST_START, TRACE_LIST_MAX);
bit = trace_test_and_set_recursion(ip, parent_ip, TRACE_LIST_START);
if (bit < 0)
return;

Expand Down Expand Up @@ -7052,7 +7052,7 @@ static void ftrace_ops_assist_func(unsigned long ip, unsigned long parent_ip,
{
int bit;

bit = trace_test_and_set_recursion(ip, parent_ip, TRACE_LIST_START, TRACE_LIST_MAX);
bit = trace_test_and_set_recursion(ip, parent_ip, TRACE_LIST_START);
if (bit < 0)
return;

Expand Down

0 comments on commit fc9b289

Please sign in to comment.