Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Concurrent execution of sub-buffer order resizing (`ring_buffer_subbuf_order_set`) and read operations (`ring_buffer_alloc_read_page`, `ring_buffer_read_page`) can lead to out-of-bounds writes, use-after-free, and page allocator corruption due to mismatched synchronization.
--
--- Patch [1]: [PATCH v4 1/9] ring-buffer: Free cpu_buffer->free_page with subbuf_order ---
commit 4100674474d6bd293fb872fecb17fc781ca58502
Author: Vincent Donnefort <[email protected]>
ring-buffer: Free cpu_buffer->free_page with subbuf_order
When sub-buffers use an order greater than 0, cpu_buffer->free_page is
allocated with subbuf_order. Use the correct order for
cpu_buffer->free_page.
> diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> index 2667992f0aa29..a3d28b2e2c94a 100644
> --- a/kernel/trace/ring_buffer.c
> +++ b/kernel/trace/ring_buffer.c
> @@ -2631,7 +2631,7 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
> free_buffer_page(bpage);
> }
>
> - free_page((unsigned long)cpu_buffer->free_page);
> + free_pages((unsigned long)cpu_buffer->free_page, cpu_buffer->buffer->subbuf_order);
[Severity: Critical]
This isn't a bug introduced by this patch, but is it possible for a race
condition between resizing and reading to lead to memory corruption?
If thread A changes the sub-buffer size via
/sys/kernel/tracing/buffer_subbuf_size_kb, ring_buffer_subbuf_order_set()
updates buffer->subbuf_order locklessly globally before tearing down the CPU
buffers and clearing their free_page.
If thread B concurrently reads /sys/kernel/tracing/per_cpu/cpu0/trace_pipe_raw
via tracing_buffers_read(), it observes the new order and calls
ring_buffer_alloc_read_page().
Because ring_buffer_subbuf_order_set() hasn't yet cleared
cpu_buffer->free_page for this CPU (they use distinct locks), thread B pops
the old page from free_page but tags it with the new order:
kernel/trace/ring_buffer.c:ring_buffer_alloc_read_page() {
...
bpage->order = buffer->subbuf_order;
...
if (cpu_buffer->free_page) {
bpage->data = cpu_buffer->free_page;
cpu_buffer->free_page = NULL;
}
...
}
Thread B then calls ring_buffer_read_page(). The lockless check passes
because both match the new order:
kernel/trace/ring_buffer.c:ring_buffer_read_page() {
...
if (data_page->order != buffer->subbuf_order)
return -1;
...
reader->page = data_page->data;
}
Could this allow the undersized old page to be swapped into the ring buffer
as the reader_page, leading to an out-of-bounds write when a writer attempts
to write large events?
It seems it could also cause page allocator corruption when a smaller page
is later passed to free_pages() with a larger order.
> kfree(cpu_buffer);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.