Re: [PATCH v4 3/9] ring-buffer: Fix subbuf resize race with ring buffer readers

Vincent Donnefort <[email protected]>
Newsgroups org.kernel.vger.linux-trace-kernel,dev.linux.lists.sashiko-reviews
Message-ID <[email protected]>
On Wed, Aug 12, 2026 at 03:53:12PM +0000, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> 
> New issues:
> - [High] Use-After-Free dereference of `reader_page` in `ring_buffer_read_start()` when bypassing buffer mutex.
> 
> Pre-existing issues:
> - [High] 32-bit integer overflow in ring buffer capacity calculation truncates large trace buffers.
> - [High] Out-of-bounds kernel memory write and VMAP flush in `ring_buffer_map_get_reader()` due to global capacity TOCTOU race.
> --
> 
> --- Patch [3]: [PATCH v4 3/9] ring-buffer: Fix subbuf resize race with ring buffer readers ---
> commit 9618f28d9a6939c645e7579fa798c42557eb075c
> Author: Vincent Donnefort <[email protected]>
> 
> ring-buffer: Fix subbuf resize race with ring buffer readers
> 
> This commit changes the trace_buffer to use bpage order instead of
> trace_buffer::subbuf_size in ring_buffer_read_start() and
> ring_buffer_read_page(). It also removes trace_buffer::subbuf_size and
> introduces capacity accessors.
> 
> > diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> > index ec4f5a0c93e85..97449423d3a68 100644
> > --- a/kernel/trace/ring_buffer.c
> > +++ b/kernel/trace/ring_buffer.c
> [ ... ]
> > @@ -6380,36 +6417,44 @@ EXPORT_SYMBOL_GPL(ring_buffer_consume);
> >  struct ring_buffer_iter *
> >  ring_buffer_read_start(struct trace_buffer *buffer, int cpu, gfp_t flags)
> >  {
> > +	struct ring_buffer_iter *iter __free(kfree) = kzalloc_obj(*iter, flags);
> >  	struct ring_buffer_per_cpu *cpu_buffer;
> > -	struct ring_buffer_iter *iter;
> > -
> > -	if (!cpumask_test_cpu(cpu, buffer->cpumask))
> > -		return NULL;
> >  
> > -	iter = kzalloc_obj(*iter, flags);
> >  	if (!iter)
> >  		return NULL;
> >  
> > -	/* Holds the entire event: data and meta data */
> > -	iter->event_size = buffer->subbuf_size;
> > -	iter->event = kmalloc(iter->event_size, flags);
> > -	if (!iter->event) {
> > -		kfree(iter);
> > +	if (!cpumask_test_cpu(cpu, buffer->cpumask))
> >  		return NULL;
> > -	}
> >  
> >  	cpu_buffer = buffer->buffers[cpu];
> >  
> > -	iter->cpu_buffer = cpu_buffer;
> > +	/*
> > +	 * Only KDB is using GFP_ATOMIC, for the others, lock the buffer to
> > +	 * prevent concurrent resizing.
> > +	 */
> > +	if (gfpflags_allow_blocking(flags))
> > +		mutex_lock(&buffer->mutex);
> >  
> >  	atomic_inc(&cpu_buffer->resize_disabled);
> >  
> > +	if (gfpflags_allow_blocking(flags))
> > +		mutex_unlock(&buffer->mutex);
> > +
> > +	/* Holds the entire event: data and meta data. */
> > +	iter->event_size = rb_page_capacity(READ_ONCE(cpu_buffer->reader_page));
> 
> [Severity: High]
> Can this result in a use-after-free dereference of the reader page?
> 
> When ring_buffer_read_start() is called with GFP_ATOMIC, it skips acquiring
> buffer->mutex and only increments cpu_buffer->resize_disabled.
> 
> If a concurrent ring_buffer_subbuf_order_set() has already passed its
> resize_disabled check, it will proceed to allocate new pages and free the old
> reader page.
> 
> A thread executing ring_buffer_read_start() could load the old reader page
> pointer just before it is freed by the other CPU, dereferencing freed memory
> when accessing the order field.

Yep, as the comment says, we only bypass for KDB.

> 
> > +	iter->event = kmalloc(iter->event_size, flags);
> > +	if (!iter->event) {
> > +		atomic_dec(&cpu_buffer->resize_disabled);
> > +		return NULL;
> > +	}
> [ ... ]
> > @@ -7367,8 +7413,8 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
> >  		}
> >  
> >  		/* Update the number of pages to match the new size */
> > -		nr_pages = old_size * buffer->buffers[cpu]->nr_pages;
> > -		nr_pages = DIV_ROUND_UP(nr_pages, buffer->subbuf_size);
> > +		nr_pages = old_capacity * buffer->buffers[cpu]->nr_pages;
> 
> [Severity: High]
> This is a pre-existing issue, but can this 32-bit integer capacity calculation
> truncate large trace buffers?
> 
> For buffers larger than 4GB, the multiplication of old_capacity and
> buffer->buffers[cpu]->nr_pages can exceed 32 bits. Because nr_pages is a
> 32-bit signed integer, the result is truncated, and the buffer is shrunk to a
> tiny fraction of its intended size during a resize operation.

This is fixed in a later patch.

> 
> > +		nr_pages = DIV_ROUND_UP(nr_pages, rb_subbuf_capacity(buffer));
> >  
> >  		/* we need a minimum of two pages */
> >  		if (nr_pages < 2)
> [ ... ]
> > @@ -7896,7 +7941,7 @@ int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu)
> >  			 * missed events, then record it there.
> >  			 */
> >  			commit = rb_page_size(reader);
> > -			if (buffer->subbuf_size - commit >= sizeof(missed_events)) {
> > +			if (rb_subbuf_capacity(buffer) - commit >= sizeof(missed_events)) {
> 
> [Severity: High]
> This is a pre-existing issue, but does this create a time-of-check to
> time-of-use race leading to an out-of-bounds kernel memory write and
> VMAP flush?

This is fixed in a later patch.

> 
> During a sub-buffer order resize, ring_buffer_subbuf_order_set() modifies
> buffer->subbuf_order globally before verifying if resize_disabled is set. If
> the buffer is currently mapped, the resize aborts and restores the old order.
> 
> In that race window, a concurrent ring_buffer_map_get_reader() call will see
> the new, larger capacity via rb_subbuf_capacity(buffer) here. It could then
> write missed_events past the end of the old, smaller reader page, and
> subsequently flush memory beyond the mapped VMAP range.
> 
> >  				memcpy(&dpage->data[commit], &missed_events,
> >  				       sizeof(missed_events));
> >  				local_add(RB_MISSED_STORED, &dpage->commit);
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3

-- 
Vincent
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.