From 2b2dc9dd356aed030e7ac11469d12f736793bc89 Mon Sep 17 00:00:00 2001 From: Wang Wensheng Date: Sun, 4 Oct 2026 15:57:55 +0800 Subject: [PATCH 1/3] printk_ringbuffer: Fix space_used() when blk_lpos->next overflows Since commit 67e1b0052f6b ("printk_ringbuffer: don't needlessly wrap data blocks around") blk_lpos is treated as not wrapped when blk_lpos->next overflows to the start of data_ring. We use the indices of blk_lpos->next and blk_lpos->start to calculate the size of the data_block. This computes zero minus a small positive value, resulting in underflow and an unexpectedly large value. Simply using the original lpos with wrap-counts solves the problem. Fixes: 67e1b0052f6b ("printk_ringbuffer: don't needlessly wrap data blocks around") Signed-off-by: Wang Wensheng Reviewed-by: John Ogness Reviewed-by: Petr Mladek Link: https://patch.msgid.link/20261004075755.363989-1-wsw9603@163.com Signed-off-by: Petr Mladek --- kernel/printk/printk_ringbuffer.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/kernel/printk/printk_ringbuffer.c b/kernel/printk/printk_ringbuffer.c index d38e7c2f6887..46a79ec0940a 100644 --- a/kernel/printk/printk_ringbuffer.c +++ b/kernel/printk/printk_ringbuffer.c @@ -1237,11 +1237,9 @@ static unsigned int space_used(struct prb_data_ring *data_ring, if (BLK_DATALESS(blk_lpos)) return 0; - if (!is_blk_wrapped(data_ring, blk_lpos->begin, blk_lpos->next)) { - /* Data block does not wrap. */ - return (DATA_INDEX(data_ring, blk_lpos->next) - - DATA_INDEX(data_ring, blk_lpos->begin)); - } + /* Data block does not wrap. */ + if (!is_blk_wrapped(data_ring, blk_lpos->begin, blk_lpos->next)) + return blk_lpos->next - blk_lpos->begin; /* * For wrapping data blocks, the trailing (wasted) space is From 9ead26a7593e08c3f3661f29f24e6d002133a92b Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Mon, 26 Oct 2026 17:01:28 +0100 Subject: [PATCH 2/3] MAINTAINERS: Add new "printk" mailing list for PRINTK, PRINTK INDEXING, and VSPRINTF Reference the new "printk@lists.linux.dev" mailing list for PRINTK, PRINTK INDEXING, and VSPRINTF subsystems. Acked-by: Steven Rostedt Reviewed-by: John Ogness Acked-by: Chris Down Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20261026160128.12581-1-pmladek@suse.com Signed-off-by: Petr Mladek --- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9388da2a2737..72372c52e804 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21964,6 +21964,7 @@ M: Petr Mladek M: John Ogness R: Steven Rostedt R: Sergey Senozhatsky +L: printk@lists.linux.dev S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux.git F: Documentation/core-api/printk-basics.rst @@ -21972,6 +21973,7 @@ F: kernel/printk/ PRINTK INDEXING R: Chris Down +L: printk@lists.linux.dev S: Maintained F: Documentation/core-api/printk-index.rst F: kernel/printk/index.c @@ -29491,6 +29493,7 @@ M: Steven Rostedt R: Andy Shevchenko R: Rasmus Villemoes R: Sergey Senozhatsky +L: printk@lists.linux.dev S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux.git F: Documentation/core-api/printk-formats.rst From 8a7f26aa072a4e63bc3b1adfe649546709db722e Mon Sep 17 00:00:00 2001 From: John Ogness Date: Fri, 30 Oct 2026 15:03:55 +0106 Subject: [PATCH 3/3] printk: Avoid multiple unregistration in unregister_console_locked() unregister_console_locked() is called from within hlist_for_each_entry_safe(console_list) loops. This would be safe if unregister_console_locked() only unregistered the specified console. After all, that is what the _safe variant is for. However, unregister_console_locked() calls printk_kthreads_check_locked(), which can unregister _multiple_ consoles if kthreads fail to start. This could lead to hlist_for_each_entry_safe() use-after-free scenarios because not only was the list entry removed, but possibly the following entry as well (the @n temporary storage of the next entry). Move the call to printk_kthreads_check_locked() outside of unregister_console_locked(). Add comments to both functions to clarify if they are safe to be used in hlist_for_each_entry_safe(console_list) loops. Reported-by: Sashiko Closes: https://lore.kernel.org/lkml/87ik31iz4x.fsf@jogness.linutronix.de Fixes: 76f258bf3f2a ("printk: nbcon: Introduce printer kthreads") Signed-off-by: John Ogness Reviewed-by: Petr Mladek Tested-by: Derek Barbosa Link: https://patch.msgid.link/20261030135802.663893-1-john.ogness@linutronix.de Signed-off-by: Petr Mladek --- kernel/printk/printk.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 9a205f94bfac..5dbcf8a9909b 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -3792,6 +3792,13 @@ static struct syscore printk_syscore = { * If appropriate, start nbcon kthreads and set @printk_kthreads_running. * If any kthreads fail to start, those consoles are unregistered. * + * Called after consoles are registered or unregistered. + * + * This MUST NOT be called within an hlist_for_each_entry_safe(console_list) + * loop because it can unregister multiple consoles if any kthreads fail to + * start. That would lead to use-after-free scenarios within the + * hlist_for_each_entry_safe(console_list) loop of the caller. + * * Must be called under console_list_lock(). */ static void printk_kthreads_check_locked(void) @@ -4230,7 +4237,20 @@ void register_console(struct console *newcon) } EXPORT_SYMBOL(register_console); -/* Must be called under console_list_lock(). */ +/* + * Unregister the specified console. + * + * This can be called safely from within an + * hlist_for_each_entry_safe(console_list) loop because it unregisters + * _only_ the specified console. + * + * Afterwards the caller should call printk_kthreads_check_locked() since + * unregistering a console may require printer threads to start/stop. Note + * that printk_kthreads_check_locked() cannot be called safely from within + * an hlist_for_each_entry_safe(console_list) loop. + * + * Must be called under console_list_lock(). + */ static int unregister_console_locked(struct console *console) { bool use_device_lock = (console->flags & CON_NBCON) && console->write_atomic; @@ -4322,9 +4342,6 @@ static int unregister_console_locked(struct console *console) if (console->exit) res = console->exit(console); - /* Changed console list, may require printer threads to start/stop. */ - printk_kthreads_check_locked(); - return res; } @@ -4333,7 +4350,12 @@ int unregister_console(struct console *console) int res; console_list_lock(); + res = unregister_console_locked(console); + + /* Changed console list, may require printer threads to start/stop. */ + printk_kthreads_check_locked(); + console_list_unlock(); return res; } @@ -4472,6 +4494,10 @@ static int __init printk_late_init(void) unregister_console_locked(con); } + + /* Changed console list, may require printer threads to start/stop. */ + printk_kthreads_check_locked(); + console_list_unlock(); ret = cpuhp_setup_state_nocalls(CPUHP_PRINTK_DEAD, "printk:dead", NULL,