From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
To: Vincent Donnefort <vdonnefort@google.com>
Cc: Steven Rostedt <rostedt@goodmis.org>,
"Masami Hiramatsu (Google)" <mhiramat@kernel.org>,
Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org,
kernel-team@android.com
Subject: Re: [PATCH] ring-buffer: Fix race between ring_buffer_subbuf_order_set() and readers
Date: Sat, 15 Aug 2026 01:00:38 +0900 [thread overview]
Message-ID: <20260815010038.a75cb3b411c7b846fb162c99@kernel.org> (raw)
In-Reply-To: <an8r9yMinscYpmjA@google.com>
On Fri, 14 Aug 2026 15:53:43 +0100
Vincent Donnefort <vdonnefort@google.com> wrote:
> On Fri, Aug 14, 2026 at 10:42:08AM -0400, Steven Rostedt wrote:
> > On Fri, 14 Aug 2026 01:16:13 +0900
> > "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> >
> > > From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > >
> > > When ring_buffer_subbuf_order_set() updates buffer->subbuf_order, it
> > > previously modified buffer->subbuf_order before clearing the cached
> > > per-CPU free_page entries. Furthermore, clearing cpu_buffer->free_page
> > > was done under cpu_buffer->reader_lock, whereas
> > > ring_buffer_alloc_read_page() protects cpu_buffer->free_page using
> > > arch_spin_lock(&cpu_buffer->lock).
> > >
> > > Because ring_buffer_alloc_read_page(), ring_buffer_free_read_page(),
> > > and ring_buffer_read_page() checked buffer->subbuf_order locklessly
> > > before accessing reader resources, a TOCTOU race allowed a concurrent
> > > reader to obtain, cache, or swap a page allocated under an outdated order
> > > while tagging bpage->order with the new order. This allowed undersized
> > > pages to be swapped into the ring buffer, leading to heap buffer overflows,
> > > or caused free_pages() to be called with an invalid order.
> > >
> > > Fix this by:
> > > 1. Flushing and freeing all per-CPU cached free_page entries under
> > > arch_spin_lock(&cpu_buffer->lock) using old_order before modifying
> > > buffer->subbuf_order.
> > > 2. Protecting bpage->order assignment under
> > > arch_spin_lock(&cpu_buffer->lock) in ring_buffer_alloc_read_page().
> > > 3. Moving the buffer->subbuf_order validation inside
> > > arch_spin_lock(&cpu_buffer->lock) in ring_buffer_free_read_page().
> > > 4. Re-validating buffer->subbuf_order inside reader_lock in
> > > ring_buffer_read_page().
> > > 5. Protecting cpu_buffer->free_page extraction with
> > > arch_spin_lock(&cpu_buffer->lock) in ring_buffer_subbuf_order_set().
> > >
> > > Fixes: 2808e31ec12e ("ring-buffer: Add interface for configuring trace sub buffer size")
> > > Assisted-by: Antigravity:gemini-3.6-flash
> > > Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > > ---
> >
> >
> > Please rebase on top of ring-buffer/for-next, as I added Vincent's patches to that.
> >
> > > kernel/trace/ring_buffer.c | 35 +++++++++++++++++++++++++++++------
> > > 1 file changed, 29 insertions(+), 6 deletions(-)
> > >
> > > diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> > > index 2667992f0aa2..6d180689ad59 100644
> > > --- a/kernel/trace/ring_buffer.c
> > > +++ b/kernel/trace/ring_buffer.c
> > > @@ -6957,11 +6957,11 @@ ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu)
> > > if (!bpage)
> > > return ERR_PTR(-ENOMEM);
> > >
> > > - bpage->order = buffer->subbuf_order;
> > > cpu_buffer = buffer->buffers[cpu];
> > > local_irq_save(flags);
> > > arch_spin_lock(&cpu_buffer->lock);
> > >
> > > + bpage->order = buffer->subbuf_order;
> >
> > This is still needed.
>
> I am not sure, the lock is per-cpu_buffer but subbuf_order. is global to
> trace_buffer?
Yeah, we don't need this.
I got new ring_buffer_read_page() ensures the data_page->order ==
cpu_buffer->reader_page->order. This is a bit tricky but it allows us to
asynchronously allocate reader_page in different order outside of locking.
(and if free_page exists, it is copied with its order, so it is safe.)
>
> >
> > > if (cpu_buffer->free_page) {
> > > bpage->data = cpu_buffer->free_page;
> > > cpu_buffer->free_page = NULL;
> > > @@ -7010,13 +7010,13 @@ void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu,
> > > * is different from the subbuffer order of the buffer -
> > > * we can't reuse it
> > > */
> > > - if (page_ref_count(page) > 1 || data_page->order != buffer->subbuf_order)
> > > + if (page_ref_count(page) > 1)
> > > goto out;
> > >
> > > local_irq_save(flags);
> > > arch_spin_lock(&cpu_buffer->lock);
> > >
> > > - if (!cpu_buffer->free_page) {
> > > + if (data_page->order == buffer->subbuf_order && !cpu_buffer->free_page) {
> >
> > Swap the order please. It has to check both to continue and if one fails it
> > will not continue. Checking for cpu_buffer->free_page to be NULL first is
> > the quicker check. And also the more likely one to fail.
> >
> > > cpu_buffer->free_page = dpage;
> > > dpage = NULL;
> > > }
> > > @@ -7094,15 +7094,15 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
> > > if (!data_page || !data_page->data)
> > > return -1;
> > >
> > > - if (data_page->order != buffer->subbuf_order)
> > > - return -1;
> > > -
> > > dpage = data_page->data;
> > > if (!dpage)
> > > return -1;
> > >
> > > guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock);
> > >
> > > + if (data_page->order != buffer->subbuf_order)
> > > + return -1;
> > > +
>
> I have modified this as part of
>
> tracing: Fix subbuf resize races with trace_pipe_raw readers
Got it. Yours changes the data structure. Mine is for older kernels
as a minimal change.
>
> > > reader = rb_get_reader_page(cpu_buffer);
> > > if (!reader)
> > > return -1;
> > > @@ -7350,6 +7350,27 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
> > > /* Make sure all commits have finished */
> > > synchronize_rcu();
> > >
> > > + /* Flush any cached free_page allocated with old_order */
> > > + for_each_buffer_cpu(buffer, cpu) {
> > > + struct buffer_data_page *old_free;
> > > + unsigned long flags;
> > > +
> > > + if (!cpumask_test_cpu(cpu, buffer->cpumask))
> > > + continue;
> > > +
> > > + cpu_buffer = buffer->buffers[cpu];
> > > +
> > > + local_irq_save(flags);
> > > + arch_spin_lock(&cpu_buffer->lock);
> > > + old_free = cpu_buffer->free_page;
> > > + cpu_buffer->free_page = NULL;
> > > + arch_spin_unlock(&cpu_buffer->lock);
> > > + local_irq_restore(flags);
> > > +
> > > + if (old_free)
> > > + free_pages((unsigned long)old_free, old_order);
> > > + }
> >
> > Honestly, this should be a separate patch. The first part of this patch is
> > data races with adding and freeing, but this is about changes to the size.
Since Vincent's patch[1] handles data page pointer with its order, we don't
this part anymore.
[1] ring-buffer: Make cpu_buffer::free_page a buffer_data_read_page
So I confirmed Vincent's patches with Fixed tag are fixing the racing
problem in the different way.
Thank you,
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
next prev parent reply other threads:[~2026-08-14 16:00 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-13 16:16 [PATCH 0/1] ring-buffer: Fix sub-buffer order updates and free_page leaks Masami Hiramatsu (Google)
2026-08-13 16:16 ` [PATCH] ring-buffer: Fix race between ring_buffer_subbuf_order_set() and readers Masami Hiramatsu (Google)
2026-08-13 16:30 ` sashiko-bot
2026-08-14 14:42 ` Steven Rostedt
2026-08-14 14:53 ` Vincent Donnefort
2026-08-14 16:00 ` Masami Hiramatsu [this message]
2026-08-13 17:25 ` [PATCH 0/1] ring-buffer: Fix sub-buffer order updates and free_page leaks Vincent Donnefort
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260815010038.a75cb3b411c7b846fb162c99@kernel.org \
--to=mhiramat@kernel.org \
--cc=kernel-team@android.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=mathieu.desnoyers@efficios.com \
--cc=rostedt@goodmis.org \
--cc=vdonnefort@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.