Thread (27 messages) 27 messages, 4 authors, 1d ago

Re: [PATCH v4 2/9] ring-buffer: Hold cpu_buffer::lock when resizing a subbuf

From: Vincent Donnefort <hidden>
Date: 2026-08-12 16:41:57
Also in: sashiko-reviews

On Wed, Aug 12, 2026 at 03:46:58PM +0000, sashiko-bot@kernel.org wrote:
quoted hunk ↗ jump to hunk
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [Critical] The patch attempts to fix a race with `cpu_buffer->free_page` by adding an `arch_spin_lock(&cpu_buffer->lock)`, but it leaves a severe Time-Of-Check to Time-Of-Use (TOCTOU) race unaddressed. `buffer->subbuf_order` and `buffer->subbuf_size` are updated globally outside of any per-CPU lock. If a reader (like `tracing_buffers_splice_read`) calls `ring_buffer_alloc_read_page()` concurrently, it locklessly reads the NEW `buffer->subbuf_order` but then retrieves the OLD, incorrectly-sized `free_page` from the locked cache. When `ring_buffer_read_page()` uses this page, it copies data up to the NEW `buffer->subbuf_size`, leading to a heap buffer overflow if the new order is larger. Similarly, `ring_buffer_free_read_page()` validates `data_page->order` locklessly; if the global order changes just after the check, it inserts a new-order page into `free_page`, which `ring_buffer_subbuf_order_set()` will later free using the `old_order`, corrupting the page allocator.
- [High] The patch adds an unconditional `arch_spin_lock(&cpu_buffer->lock)` to `ring_buffer_subbuf_order_set()`. This creates an NMI deadlock window. If an NMI (e.g., from a watchdog or an Oops triggering `ftrace_dump_on_oops`) fires on the same CPU while this lock is held, the NMI handler may attempt to read the ring buffer. The read path (via `ring_buffer_peek()` -> `rb_buffer_peek()` -> `__rb_get_reader_page()`) unconditionally acquires the same `cpu_buffer->lock` using `arch_spin_lock()` without an NMI-safe trylock fallback. Because the interrupted context holds the lock, the NMI handler will spin forever, causing a hard lockup.
--
--- Patch [2]: [PATCH v4 2/9] ring-buffer: Hold cpu_buffer::lock when resizing a subbuf ---
commit 1db68f92346fd21f55d54529fb97b920f02a0136
Author: Vincent Donnefort [off-list ref]

ring-buffer: Hold cpu_buffer::lock when resizing a subbuf

This commit adds an arch_spin_lock() around the clearing of
cpu_buffer->free_page in ring_buffer_subbuf_order_set(). This aims to
prevent races with ring_buffer_alloc_read_page() and
ring_buffer_free_read_page() during sub-buffer resizing.
quoted
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index a3d28b2e2c94a..ec4f5a0c93e85 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -7431,8 +7431,10 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
 		cpu_buffer->nr_pages = cpu_buffer->nr_pages_to_update;
 		cpu_buffer->nr_pages_to_update = 0;
 
+		arch_spin_lock(&cpu_buffer->lock);
[Severity: Critical]
This is a pre-existing issue, but is there a Time-Of-Check to Time-Of-Use
(TOCTOU) race between the global order updates and this per-CPU lock?

Earlier in ring_buffer_subbuf_order_set(), buffer->subbuf_order and
buffer->subbuf_size are updated globally outside of any per-CPU lock.

If a reader like tracing_buffers_splice_read() calls
ring_buffer_alloc_read_page() concurrently, can it locklessly read the new
buffer->subbuf_order, but then retrieve the old, incorrectly-sized
free_page from the locked cache?

When ring_buffer_read_page() uses this page, wouldn't it copy data up to
the new buffer->subbuf_size, leading to a heap buffer overflow if the new
order is larger?

Similarly, ring_buffer_free_read_page() validates data_page->order against
buffer->subbuf_order locklessly. If the global order changes right after
the check, could it insert a new-order page into free_page, which
ring_buffer_subbuf_order_set() will later free using the old order,
corrupting the page allocator?
This is hopefully fixed in one of the following patch of this series.
[Severity: High]
This is also a pre-existing issue, but does adding this unconditional
arch_spin_lock() widen an NMI deadlock window?

If an NMI (such as a watchdog or an Oops triggering ftrace_dump_on_oops)
fires on the same CPU while this lock is held, the NMI handler may attempt
to read the ring buffer.

The read path via __rb_get_reader_page() unconditionally acquires the same
cpu_buffer->lock using arch_spin_lock() without an NMI-safe trylock
fallback.
This is under raw_spin_lock_irqsave(). I believe the panic path will first try
to issue normal IPIs.

-- 
Vincent
Because the interrupted context holds the lock, will the NMI handler spin
forever, causing a hard lockup?
quoted
 		old_free_data_page = cpu_buffer->free_page;
 		cpu_buffer->free_page = NULL;
+		arch_spin_unlock(&cpu_buffer->lock);
 
 		rb_head_page_activate(cpu_buffer);
-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260812153311.2328812-1-vdonnefort@google.com?part=2
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help