diff --git a/kernel/panic.c b/kernel/panic.c index 75368738d32d..5d498ff8a18b 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -802,7 +802,7 @@ EXPORT_SYMBOL(panic); * small shell script that prints the TAINT_FLAGS_COUNT bits of * /proc/sys/kernel/tainted. * - * Also, update TAINT_BUF_MAX below. + * Also, update INIT_TAINT_BUF_MAX below. */ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = { TAINT_FLAG(PROPRIETARY_MODULE, 'P', 'G'), @@ -856,17 +856,58 @@ static void print_tainted_seq(struct seq_buf *s, bool verbose) } } -/* 350 can accommodate all taint flags in verbose mode, with some headroom */ -#define TAINT_BUF_MAX 350 +/* The initial buffer can accommodate all taint flags in verbose + * mode, with some headroom. Once the allocator is available, the + * exact size is allocated dynamically; the initial buffer remains + * as a fallback if allocation fails. + * + * The verbose taint string currently requires up to 327 characters. + */ +#define INIT_TAINT_BUF_MAX 350 + +static char init_taint_buf[INIT_TAINT_BUF_MAX]; +static char *taint_buf = init_taint_buf; +static size_t taint_buf_size = INIT_TAINT_BUF_MAX; + +static __init int alloc_taint_buf(void) +{ + int i; + char *buf; + size_t size = 0; + + size += sizeof("Tainted: ") - 1; + for (i = 0; i < TAINT_FLAGS_COUNT; i++) { + size += 2; /* For ", " */ + size += 4; /* For "[%c]=" */ + size += strlen(taint_flags[i].desc); + } + + size += 1; /* For NULL terminator */ + + buf = kmalloc(size, GFP_KERNEL); + + if (!buf) { + /* Allocation may fail; this warning explains possibly + * truncated taint strings + */ + pr_warn_once("taint string buffer allocation failed, using fallback buffer\n"); + return 0; + } + + taint_buf = buf; + taint_buf_size = size; + + return 0; +} +postcore_initcall(alloc_taint_buf); static const char *_print_tainted(bool verbose) { - static char buf[TAINT_BUF_MAX]; struct seq_buf s; BUILD_BUG_ON(ARRAY_SIZE(taint_flags) != TAINT_FLAGS_COUNT); - seq_buf_init(&s, buf, sizeof(buf)); + seq_buf_init(&s, taint_buf, taint_buf_size); print_tainted_seq(&s, verbose);