Skip to content

Commit 002eb6a

Browse files
jognesspmladek
authored andcommitted
printk: track/limit recursion
Currently the printk safe buffers provide a form of recursion protection by redirecting to the safe buffers whenever printk() is recursively called. In preparation for removal of the safe buffers, provide an alternate explicit recursion protection. Recursion is limited to 3 levels per-CPU and per-context. Signed-off-by: John Ogness <[email protected]> Reviewed-by: Petr Mladek <[email protected]> Signed-off-by: Petr Mladek <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 55d6af1 commit 002eb6a

File tree

1 file changed

+83
-3
lines changed

1 file changed

+83
-3
lines changed

kernel/printk/printk.c

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1940,6 +1940,76 @@ static void call_console_drivers(const char *ext_text, size_t ext_len,
19401940
}
19411941
}
19421942

1943+
/*
1944+
* Recursion is tracked separately on each CPU. If NMIs are supported, an
1945+
* additional NMI context per CPU is also separately tracked. Until per-CPU
1946+
* is available, a separate "early tracking" is performed.
1947+
*/
1948+
static DEFINE_PER_CPU(u8, printk_count);
1949+
static u8 printk_count_early;
1950+
#ifdef CONFIG_HAVE_NMI
1951+
static DEFINE_PER_CPU(u8, printk_count_nmi);
1952+
static u8 printk_count_nmi_early;
1953+
#endif
1954+
1955+
/*
1956+
* Recursion is limited to keep the output sane. printk() should not require
1957+
* more than 1 level of recursion (allowing, for example, printk() to trigger
1958+
* a WARN), but a higher value is used in case some printk-internal errors
1959+
* exist, such as the ringbuffer validation checks failing.
1960+
*/
1961+
#define PRINTK_MAX_RECURSION 3
1962+
1963+
/*
1964+
* Return a pointer to the dedicated counter for the CPU+context of the
1965+
* caller.
1966+
*/
1967+
static u8 *__printk_recursion_counter(void)
1968+
{
1969+
#ifdef CONFIG_HAVE_NMI
1970+
if (in_nmi()) {
1971+
if (printk_percpu_data_ready())
1972+
return this_cpu_ptr(&printk_count_nmi);
1973+
return &printk_count_nmi_early;
1974+
}
1975+
#endif
1976+
if (printk_percpu_data_ready())
1977+
return this_cpu_ptr(&printk_count);
1978+
return &printk_count_early;
1979+
}
1980+
1981+
/*
1982+
* Enter recursion tracking. Interrupts are disabled to simplify tracking.
1983+
* The caller must check the boolean return value to see if the recursion is
1984+
* allowed. On failure, interrupts are not disabled.
1985+
*
1986+
* @recursion_ptr must be a variable of type (u8 *) and is the same variable
1987+
* that is passed to printk_exit_irqrestore().
1988+
*/
1989+
#define printk_enter_irqsave(recursion_ptr, flags) \
1990+
({ \
1991+
bool success = true; \
1992+
\
1993+
typecheck(u8 *, recursion_ptr); \
1994+
local_irq_save(flags); \
1995+
(recursion_ptr) = __printk_recursion_counter(); \
1996+
if (*(recursion_ptr) > PRINTK_MAX_RECURSION) { \
1997+
local_irq_restore(flags); \
1998+
success = false; \
1999+
} else { \
2000+
(*(recursion_ptr))++; \
2001+
} \
2002+
success; \
2003+
})
2004+
2005+
/* Exit recursion tracking, restoring interrupts. */
2006+
#define printk_exit_irqrestore(recursion_ptr, flags) \
2007+
do { \
2008+
typecheck(u8 *, recursion_ptr); \
2009+
(*(recursion_ptr))--; \
2010+
local_irq_restore(flags); \
2011+
} while (0)
2012+
19432013
int printk_delay_msec __read_mostly;
19442014

19452015
static inline void printk_delay(void)
@@ -2040,11 +2110,14 @@ int vprintk_store(int facility, int level,
20402110
struct prb_reserved_entry e;
20412111
enum log_flags lflags = 0;
20422112
struct printk_record r;
2113+
unsigned long irqflags;
20432114
u16 trunc_msg_len = 0;
20442115
char prefix_buf[8];
2116+
u8 *recursion_ptr;
20452117
u16 reserve_size;
20462118
va_list args2;
20472119
u16 text_len;
2120+
int ret = 0;
20482121
u64 ts_nsec;
20492122

20502123
/*
@@ -2055,6 +2128,9 @@ int vprintk_store(int facility, int level,
20552128
*/
20562129
ts_nsec = local_clock();
20572130

2131+
if (!printk_enter_irqsave(recursion_ptr, irqflags))
2132+
return 0;
2133+
20582134
/*
20592135
* The sprintf needs to come first since the syslog prefix might be
20602136
* passed in as a parameter. An extra byte must be reserved so that
@@ -2092,7 +2168,8 @@ int vprintk_store(int facility, int level,
20922168
prb_commit(&e);
20932169
}
20942170

2095-
return text_len;
2171+
ret = text_len;
2172+
goto out;
20962173
}
20972174
}
20982175

@@ -2108,7 +2185,7 @@ int vprintk_store(int facility, int level,
21082185

21092186
prb_rec_init_wr(&r, reserve_size + trunc_msg_len);
21102187
if (!prb_reserve(&e, prb, &r))
2111-
return 0;
2188+
goto out;
21122189
}
21132190

21142191
/* fill message */
@@ -2130,7 +2207,10 @@ int vprintk_store(int facility, int level,
21302207
else
21312208
prb_final_commit(&e);
21322209

2133-
return (text_len + trunc_msg_len);
2210+
ret = text_len + trunc_msg_len;
2211+
out:
2212+
printk_exit_irqrestore(recursion_ptr, irqflags);
2213+
return ret;
21342214
}
21352215

21362216
asmlinkage int vprintk_emit(int facility, int level,

0 commit comments

Comments
 (0)