* [PATCH v8 0/3] ring-buffer: Fixes for subbuf resizing and persistent buffers
@ 2026-08-26 9:45 Vincent Donnefort
2026-08-26 9:45 ` [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers Vincent Donnefort
` (2 more replies)
0 siblings, 3 replies; 15+ messages in thread
From: Vincent Donnefort @ 2026-08-26 9:45 UTC (permalink / raw)
To: rostedt, mhiramat, linux-trace-kernel
Cc: mathieu.desnoyers, kernel-team, linux-kernel, Vincent Donnefort
This series addresses multiple issues discovered with the dynamic ring
buffer resizing.
I have managed to reproduce a ring_buffer_read_page() race with
$ while true; do for i in 8 16 32; do echo $i > /sys/kernel/tracing/buffer_subbuf_size_kb; sleep 0.1; done; done
Changelog:
v8:
- ring_buffer_read_page() handles gracefully read_page/reader_page
order mismatch (Steven)
v7 (https://lore.kernel.org/linux-trace-kernel/20260817101533.1558223-1-vdonnefort@google.com/):
- Match the "static" rb limit with bpage::id bitwidth
- Cover another 32-bit truncation in rb_range_buffer (Sashiko)
- Fix uninitialized spare_size (Sashiko)
v6 (https://lore.kernel.org/all/20260814154823.755406-1-vdonnefort@google.com/):
- New prototype for ring_buffer_alloc_read_page() (Steven)
- ring_buffer_read_page() to return -EAGAIN (Steven)
- Keep nr_pages "unsigned long" (Steven)
- Repase on ring-buffer/next (Drop most of the patches)
v5 (https://lore.kernel.org/all/20260813131152.3589632-1-vdonnefort@google.com/):
- Reset info->spare_read only when data is in the ring-buffer (Sashiko)
- Use `unsigned long` for subbuf_size declaration to avoid 32-bit
truncation. (Sashiko)
- Make cpu_buffer::free_page a buffer_read_data_page
- Update kerneldoc for ring_buffer_alloc_read_page()
v4 (https://lore.kernel.org/all/20260812153311.2328812-1-vdonnefort@google.com/):
- Add rb_subbuf_start() helper (Steven)
- kerneldoc additions
- Fix races in trace_pipe_raw readers
- Use rb_subbuf_capacity() in ring_buffer_subbuf_order_set()
- use rb_page_capacity() in ring_buffer_map_get_reader (Sashiko)
- Fix 32-bit overflow in ring_buffer_subbuf_order_set() (Sashiko)
- Hold cpu_buffer::lock when modifying cpu_buffer->free_page in
ring_buffer_subbuf_order_set (Sashiko)
v3 (https://lore.kernel.org/all/20260810125633.3344684-1-vdonnefort@google.com/):
- Drop first 3 patches (Rebased on 7.2-rc7)
- Add a patch to align "nr_pages" to unsigned int
- Add a patch to remove useless trace_buffer::cpus
- Add unsigned long cast for rb_subbuf_size()
- subbuf_order fix for rb_free_cpu_buffer() (Sashiko)
- Use __always_inline just like the other accessors for the hot-path.
v2 (https://lore.kernel.org/all/20260806211306.3704194-1-vdonnefort@google.com/):
- Prevent resizing of the persistent ring buffer
- Add missing bpage::order init
- Rework subbuf_size/subbuf_order (Sashiko)
- Remove ring_buffer_per_cpu::mapped
- Dynamically calculate trace_buffer::max_data_size
v1 (https://lore.kernel.org/all/20260805153225.2096152-1-vdonnefort@google.com/)
Vincent Donnefort (3):
tracing: Fix subbuf resize races with trace_pipe_raw readers
ring-buffer: Cap static ring buffer nr_pages
ring-buffer: Prevent truncation of nr_pages / nr_subbufs
include/linux/ring_buffer.h | 5 +-
kernel/trace/ring_buffer.c | 198 ++++++++++++++++++---------
kernel/trace/ring_buffer_benchmark.c | 6 +-
kernel/trace/trace.c | 94 ++++++-------
kernel/trace/trace.h | 9 +-
5 files changed, 185 insertions(+), 127 deletions(-)
base-commit: 66498c75b4f8017f62d720d9b59675bdf3abce91
--
2.55.0.860.g4b6b3295ed-goog
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers
2026-08-26 9:45 [PATCH v8 0/3] ring-buffer: Fixes for subbuf resizing and persistent buffers Vincent Donnefort
@ 2026-08-26 9:45 ` Vincent Donnefort
2026-08-26 9:59 ` sashiko-bot
2026-08-26 9:45 ` [PATCH v8 2/3] ring-buffer: Cap static ring buffer nr_pages Vincent Donnefort
2026-08-26 9:45 ` [PATCH v8 3/3] ring-buffer: Prevent truncation of nr_pages / nr_subbufs Vincent Donnefort
2 siblings, 1 reply; 15+ messages in thread
From: Vincent Donnefort @ 2026-08-26 9:45 UTC (permalink / raw)
To: rostedt, mhiramat, linux-trace-kernel
Cc: mathieu.desnoyers, kernel-team, linux-kernel, Vincent Donnefort
Concurrent subbuffer resizes may crash trace_pipe_raw readers or leak
uninitialized memory to userspace due to stale size values.
Modify ring_buffer_alloc_read_page() to handle the resizing of an
existing buffer_data_read_page if necessary and add a new
ring_buffer_read_page_size(). This new function enables ring-buffer
buffer_data_read_page users to not call the racy
ring_buffer_subbuf_size_get(). This makes the spare_size member of
ftrace_buffer_info redundant.
Finally, handle buffer_data_read_page/reader_page order discrepancy in
ring_buffer_read_page(). On a mismatch simply copy manually the data to
the buffer_data_read_page.
Fixes: bce761d75745 ("ring-buffer: Read and write to ring buffers with custom sub buffer size")
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h
index 0670742b2d60..afc7daa6ee7d 100644
--- a/include/linux/ring_buffer.h
+++ b/include/linux/ring_buffer.h
@@ -218,14 +218,15 @@ bool ring_buffer_time_stamp_abs(struct trace_buffer *buffer);
size_t ring_buffer_nr_dirty_pages(struct trace_buffer *buffer, int cpu);
struct buffer_data_read_page;
-struct buffer_data_read_page *
-ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu);
+int ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu,
+ struct buffer_data_read_page **rpage);
void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu,
struct buffer_data_read_page *page);
int ring_buffer_read_page(struct trace_buffer *buffer,
struct buffer_data_read_page *data_page,
size_t len, int cpu, int full);
void *ring_buffer_read_page_data(struct buffer_data_read_page *page);
+unsigned int ring_buffer_read_page_size(struct buffer_data_read_page *rpage);
struct trace_seq;
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 3c3ed639923d..b8e6bd309707 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -330,6 +330,11 @@ struct buffer_data_read_page {
struct buffer_data_page *data; /* actual data, stored in this page */
};
+static __always_inline unsigned int rb_read_page_capacity(struct buffer_data_read_page *rpage)
+{
+ return (PAGE_SIZE << rpage->order) - BUF_PAGE_HDR_SIZE;
+}
+
/*
* Note, the buffer_page list must be first. The buffer pages
* are allocated in cache lines, which means that each buffer
@@ -6990,56 +6995,78 @@ EXPORT_SYMBOL_GPL(ring_buffer_swap_cpu);
* ring_buffer_alloc_read_page - allocate a page to read from buffer
* @buffer: the buffer to allocate for.
* @cpu: the cpu buffer to allocate.
+ * @rpage: pointer to pass in an already allocated page (can be NULL)
+ * and returns the allocated page.
*
- * This function is used in conjunction with ring_buffer_read_page.
+ * This function is used in conjunction with ring_buffer_read_page().
* When reading a full page from the ring buffer, these functions
* can be used to speed up the process. The calling function should
* allocate a few pages first with this function. Then when it
* needs to get pages from the ring buffer, it passes the result
- * of this function into ring_buffer_read_page, which will swap
+ * of this function into ring_buffer_read_page(), which will swap
* the page that was allocated, with the read page of the buffer.
*
+ * If @rpage is provided, and it has a different order than the current
+ * subbuffer order, its payload will be freed and re-allocated. If it
+ * already matches the order, it is simply returned.
+ *
* Returns:
- * The page allocated, or ERR_PTR
+ * 0 on success, < 0 on error
*/
-struct buffer_data_read_page *
-ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu)
+int ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu,
+ struct buffer_data_read_page **rpage)
{
struct ring_buffer_per_cpu *cpu_buffer;
- struct buffer_data_read_page *bpage = NULL;
unsigned long flags;
+ unsigned int order;
if (!cpumask_test_cpu(cpu, buffer->cpumask))
- return ERR_PTR(-ENODEV);
+ return -ENODEV;
- bpage = kzalloc_obj(*bpage);
- if (!bpage)
- return ERR_PTR(-ENOMEM);
+ if (!rpage)
+ return -EINVAL;
- bpage->order = buffer->subbuf_order;
+ order = buffer->subbuf_order;
+
+ if (*rpage) {
+ if ((*rpage)->order == order)
+ return 0;
+
+ /* We can reuse rpage, but we discard the payload */
+ free_pages((unsigned long)(*rpage)->data, (*rpage)->order);
+ (*rpage)->data = NULL;
+ } else {
+ *rpage = kzalloc_obj(**rpage);
+ if (!*rpage)
+ return -ENOMEM;
+ }
+
+ (*rpage)->order = order;
cpu_buffer = buffer->buffers[cpu];
+
local_irq_save(flags);
arch_spin_lock(&cpu_buffer->lock);
if (cpu_buffer->free_page.data) {
- *bpage = cpu_buffer->free_page;
+ **rpage = cpu_buffer->free_page;
cpu_buffer->free_page.data = NULL;
}
arch_spin_unlock(&cpu_buffer->lock);
local_irq_restore(flags);
- if (bpage->data) {
- rb_init_data_page(bpage->data);
+ if ((*rpage)->data) {
+ rb_init_data_page((*rpage)->data);
} else {
- bpage->data = alloc_cpu_data(cpu, bpage->order);
- if (!bpage->data) {
- kfree(bpage);
- return ERR_PTR(-ENOMEM);
+ (*rpage)->data = alloc_cpu_data(cpu, (*rpage)->order);
+ if (!(*rpage)->data) {
+ kfree(*rpage);
+ *rpage = NULL;
+ return -ENOMEM;
}
}
- return bpage;
+ return 0;
}
EXPORT_SYMBOL_GPL(ring_buffer_alloc_read_page);
@@ -7047,21 +7074,30 @@ EXPORT_SYMBOL_GPL(ring_buffer_alloc_read_page);
* ring_buffer_free_read_page - free an allocated read page
* @buffer: the buffer the page was allocate for
* @cpu: the cpu buffer the page came from
- * @data_page: the page to free
+ * @rpage: the buffer_dat_read_page to free
*
* Free a page allocated from ring_buffer_alloc_read_page.
*/
void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu,
- struct buffer_data_read_page *data_page)
+ struct buffer_data_read_page *rpage)
{
struct ring_buffer_per_cpu *cpu_buffer;
- struct buffer_data_page *dpage = data_page->data;
- struct page *page = virt_to_page(dpage);
+ struct buffer_data_page *dpage;
unsigned long flags;
+ struct page *page;
if (!buffer || !buffer->buffers || !buffer->buffers[cpu])
return;
+ if (!rpage)
+ return;
+
+ dpage = rpage->data;
+ if (!dpage)
+ goto out;
+
+ page = virt_to_page(dpage);
+
cpu_buffer = buffer->buffers[cpu];
/*
@@ -7069,14 +7105,14 @@ 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 || rpage->order != buffer->subbuf_order)
goto out;
local_irq_save(flags);
arch_spin_lock(&cpu_buffer->lock);
if (!cpu_buffer->free_page.data) {
- cpu_buffer->free_page = *data_page;
+ cpu_buffer->free_page = *rpage;
dpage = NULL;
}
@@ -7084,8 +7120,8 @@ void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu,
local_irq_restore(flags);
out:
- free_pages((unsigned long)dpage, data_page->order);
- kfree(data_page);
+ free_pages((unsigned long)dpage, rpage->order);
+ kfree(rpage);
}
EXPORT_SYMBOL_GPL(ring_buffer_free_read_page);
@@ -7156,10 +7192,9 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
if (!dpage)
return -1;
- guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock);
+ len = min_t(size_t, len, rb_read_page_capacity(data_page));
- if (data_page->order != cpu_buffer->reader_page->order)
- return -1;
+ guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock);
reader = rb_get_reader_page(cpu_buffer);
if (!reader)
@@ -7183,7 +7218,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
*/
if (read || (len < (size - read)) ||
cpu_buffer->reader_page == cpu_buffer->commit_page ||
- rb_is_static(cpu_buffer)) {
+ rb_is_static(cpu_buffer) ||
+ data_page->order != reader->order) {
struct buffer_data_page *rpage = cpu_buffer->reader_page->page;
unsigned int rpos = read;
unsigned int pos = 0;
@@ -7284,7 +7320,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
* missed events, then record it there.
*/
if (missed_events > 0 &&
- rb_page_capacity(reader) - size >= sizeof(missed_events)) {
+ rb_read_page_capacity(data_page) - size >= sizeof(missed_events)) {
memcpy(&dpage->data[size], &missed_events,
sizeof(missed_events));
local_add(RB_MISSED_STORED, &dpage->commit);
@@ -7304,8 +7340,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
/*
* This page may be off to user land. Zero it out here.
*/
- if (size < rb_page_capacity(reader))
- memset(&dpage->data[size], 0, rb_page_capacity(reader) - size);
+ if (size < rb_read_page_capacity(data_page))
+ memset(&dpage->data[size], 0, rb_read_page_capacity(data_page) - size);
return read;
}
@@ -7323,6 +7359,18 @@ void *ring_buffer_read_page_data(struct buffer_data_read_page *page)
}
EXPORT_SYMBOL_GPL(ring_buffer_read_page_data);
+/**
+ * ring_buffer_read_page_size - get size of the read page.
+ * @page: the page to get the size from
+ *
+ * Returns size of the page in bytes.
+ */
+unsigned int ring_buffer_read_page_size(struct buffer_data_read_page *rpage)
+{
+ return PAGE_SIZE << rpage->order;
+}
+EXPORT_SYMBOL_GPL(ring_buffer_read_page_size);
+
/**
* ring_buffer_subbuf_size_get - get size of the sub buffer.
* @buffer: the buffer to get the sub buffer size from
diff --git a/kernel/trace/ring_buffer_benchmark.c b/kernel/trace/ring_buffer_benchmark.c
index 593e3b59e42e..c3d34c0e64e2 100644
--- a/kernel/trace/ring_buffer_benchmark.c
+++ b/kernel/trace/ring_buffer_benchmark.c
@@ -104,7 +104,7 @@ static enum event_status read_event(int cpu)
static enum event_status read_page(int cpu)
{
- struct buffer_data_read_page *bpage;
+ struct buffer_data_read_page *bpage = NULL;
struct ring_buffer_event *event;
struct rb_page *rpage;
unsigned long commit;
@@ -114,8 +114,8 @@ static enum event_status read_page(int cpu)
int inc;
int i;
- bpage = ring_buffer_alloc_read_page(buffer, cpu);
- if (IS_ERR(bpage))
+ ret = ring_buffer_alloc_read_page(buffer, cpu, &bpage);
+ if (ret < 0)
return EVENT_DROPPED;
page_size = ring_buffer_subbuf_size_get(buffer);
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 3e0907aef172..a9cf76a0a3d8 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -7082,8 +7082,8 @@ ssize_t tracing_buffers_read(struct file *filp, char __user *ubuf,
{
struct ftrace_buffer_info *info = filp->private_data;
struct trace_iterator *iter = &info->iter;
+ unsigned int spare_size;
void *trace_data;
- int page_size;
ssize_t ret = 0;
ssize_t size;
@@ -7093,36 +7093,24 @@ ssize_t tracing_buffers_read(struct file *filp, char __user *ubuf,
if (iter->snapshot && tracer_uses_snapshot(iter->tr->current_trace))
return -EBUSY;
- page_size = ring_buffer_subbuf_size_get(iter->array_buffer->buffer);
-
- /* Make sure the spare matches the current sub buffer size */
+again:
if (info->spare) {
- if (page_size != info->spare_size) {
- ring_buffer_free_read_page(iter->array_buffer->buffer,
- info->spare_cpu, info->spare);
- info->spare = NULL;
- }
+ spare_size = ring_buffer_read_page_size(info->spare);
+ /* Do we have previous read data to read? */
+ if (info->read < spare_size)
+ goto read;
}
- if (!info->spare) {
- info->spare = ring_buffer_alloc_read_page(iter->array_buffer->buffer,
- iter->cpu_file);
- if (IS_ERR(info->spare)) {
- ret = PTR_ERR(info->spare);
- info->spare = NULL;
- } else {
- info->spare_cpu = iter->cpu_file;
- info->spare_size = page_size;
- }
- }
- if (!info->spare)
+ /* Make sure the read page order is aligned with the current subbuf order */
+ ret = ring_buffer_alloc_read_page(iter->array_buffer->buffer, iter->cpu_file,
+ &info->spare);
+ if (ret)
return ret;
- /* Do we have previous read data to read? */
- if (info->read < page_size)
- goto read;
+ spare_size = ring_buffer_read_page_size(info->spare);
+ info->read = spare_size;
+ info->spare_cpu = iter->cpu_file;
- again:
trace_access_lock(iter->cpu_file);
ret = ring_buffer_read_page(iter->array_buffer->buffer,
info->spare,
@@ -7148,8 +7136,9 @@ ssize_t tracing_buffers_read(struct file *filp, char __user *ubuf,
}
info->read = 0;
+
read:
- size = page_size - info->read;
+ size = spare_size - info->read;
if (size > count)
size = count;
trace_data = ring_buffer_read_page_data(info->spare);
@@ -7199,17 +7188,17 @@ int tracing_buffers_release(struct inode *inode, struct file *file)
}
struct buffer_ref {
- struct trace_buffer *buffer;
- void *page;
- int cpu;
- refcount_t refcount;
+ struct trace_buffer *buffer;
+ struct buffer_data_read_page *rpage;
+ int cpu;
+ refcount_t refcount;
};
static void buffer_ref_release(struct buffer_ref *ref)
{
if (!refcount_dec_and_test(&ref->refcount))
return;
- ring_buffer_free_read_page(ref->buffer, ref->cpu, ref->page);
+ ring_buffer_free_read_page(ref->buffer, ref->cpu, ref->rpage);
kfree(ref);
}
@@ -7270,23 +7259,12 @@ ssize_t tracing_buffers_splice_read(struct file *file, loff_t *ppos,
};
struct buffer_ref *ref;
bool woken = false;
- int page_size;
int entries, i;
ssize_t ret = 0;
if (iter->snapshot && tracer_uses_snapshot(iter->tr->current_trace))
return -EBUSY;
- page_size = ring_buffer_subbuf_size_get(iter->array_buffer->buffer);
- if (*ppos & (page_size - 1))
- return -EINVAL;
-
- if (len & (page_size - 1)) {
- if (len < page_size)
- return -EINVAL;
- len &= (~(page_size - 1));
- }
-
if (splice_grow_spd(pipe, &spd))
return -ENOMEM;
@@ -7294,7 +7272,8 @@ ssize_t tracing_buffers_splice_read(struct file *file, loff_t *ppos,
trace_access_lock(iter->cpu_file);
entries = ring_buffer_entries_cpu(iter->array_buffer->buffer, iter->cpu_file);
- for (i = 0; i < spd.nr_pages_max && len && entries; i++, len -= page_size) {
+ for (i = 0; i < spd.nr_pages_max && len && entries; i++) {
+ unsigned int page_size;
struct page *page;
int r;
@@ -7306,25 +7285,35 @@ ssize_t tracing_buffers_splice_read(struct file *file, loff_t *ppos,
refcount_set(&ref->refcount, 1);
ref->buffer = iter->array_buffer->buffer;
- ref->page = ring_buffer_alloc_read_page(ref->buffer, iter->cpu_file);
- if (IS_ERR(ref->page)) {
- ret = PTR_ERR(ref->page);
- ref->page = NULL;
+
+ ret = ring_buffer_alloc_read_page(ref->buffer, iter->cpu_file, &ref->rpage);
+ if (ret) {
kfree(ref);
break;
}
ref->cpu = iter->cpu_file;
- r = ring_buffer_read_page(ref->buffer, ref->page,
- len, iter->cpu_file, 1);
+ page_size = ring_buffer_read_page_size(ref->rpage);
+
+ r = -EINVAL;
+ if (IS_ALIGNED(*ppos, page_size) && len >= page_size) {
+ r = ring_buffer_read_page(ref->buffer, ref->rpage, len, iter->cpu_file, 1);
+ } else if (!i) {
+ /*
+ * If the first iteration fails this is an invalid userspace input.
+ * Otherwise, this is because the subbuf order has been modified. Do not
+ * report an error and finish the read.
+ */
+ ret = -EINVAL;
+ }
+
if (r < 0) {
- ring_buffer_free_read_page(ref->buffer, ref->cpu,
- ref->page);
+ ring_buffer_free_read_page(ref->buffer, ref->cpu, ref->rpage);
kfree(ref);
break;
}
- page = virt_to_page(ring_buffer_read_page_data(ref->page));
+ page = virt_to_page(ring_buffer_read_page_data(ref->rpage));
spd.pages[i] = page;
spd.partial[i].len = page_size;
@@ -7332,6 +7321,7 @@ ssize_t tracing_buffers_splice_read(struct file *file, loff_t *ppos,
spd.partial[i].private = (unsigned long)ref;
spd.nr_pages++;
*ppos += page_size;
+ len -= page_size;
entries = ring_buffer_entries_cpu(iter->array_buffer->buffer, iter->cpu_file);
}
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 74a7a50d1e78..203d098ee14e 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -745,11 +745,10 @@ static inline int tracing_get_cpu(struct inode *inode)
void tracing_reset_cpu(struct array_buffer *buf, int cpu);
struct ftrace_buffer_info {
- struct trace_iterator iter;
- void *spare;
- unsigned int spare_cpu;
- unsigned int spare_size;
- unsigned int read;
+ struct trace_iterator iter;
+ struct buffer_data_read_page *spare;
+ unsigned int spare_cpu;
+ unsigned int read;
};
/**
--
2.55.0.860.g4b6b3295ed-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v8 2/3] ring-buffer: Cap static ring buffer nr_pages
2026-08-26 9:45 [PATCH v8 0/3] ring-buffer: Fixes for subbuf resizing and persistent buffers Vincent Donnefort
2026-08-26 9:45 ` [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers Vincent Donnefort
@ 2026-08-26 9:45 ` Vincent Donnefort
2026-08-26 9:45 ` [PATCH v8 3/3] ring-buffer: Prevent truncation of nr_pages / nr_subbufs Vincent Donnefort
2 siblings, 0 replies; 15+ messages in thread
From: Vincent Donnefort @ 2026-08-26 9:45 UTC (permalink / raw)
To: rostedt, mhiramat, linux-trace-kernel
Cc: mathieu.desnoyers, kernel-team, linux-kernel, Vincent Donnefort
Static ring buffers (i.e. persistent, user-mapped and remote) rely on
the bpage::id field. The number of pages for those ring buffers must fit
into that variable. Enforce this limit on ring buffer creation or
user-mapping.
While at it, make buffer_page::id 31 bits. This does not change the
struct buffer_page size.
Fixes: be68d63a139b ("ring-buffer: Add ring_buffer_alloc_range()")
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index b8e6bd309707..6089fcc67e2b 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -350,7 +350,7 @@ struct buffer_page {
local_t entries; /* entries on this page */
unsigned long real_end; /* real end of data */
unsigned order; /* order of the page */
- u32 id:30; /* ID for external mapping */
+ u32 id:31; /* ID for external mapping */
u32 range:1; /* Mapped via a range */
struct buffer_data_page *page; /* Actual data page */
};
@@ -657,6 +657,15 @@ static bool rb_is_static(struct ring_buffer_per_cpu *cpu_buffer)
return cpu_buffer->user_mapped || cpu_buffer->remote || cpu_buffer->ring_meta;
}
+static unsigned long rb_static_max_pages(void)
+{
+ /*
+ * Static ring buffers are using bpage::id and must account for the
+ * reader page.
+ */
+ return (1UL << 31) - 1;
+}
+
struct ring_buffer_iter {
struct ring_buffer_per_cpu *cpu_buffer;
unsigned long head;
@@ -2842,6 +2851,10 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
*/
nr_pages = (size - sizeof(struct ring_buffer_cpu_meta)) /
(subbuf_size + sizeof(int));
+
+ if (nr_pages > rb_static_max_pages())
+ goto fail_free_buffers;
+
/* Need at least two pages plus the reader page */
if (nr_pages < 3)
goto fail_free_buffers;
@@ -2874,6 +2887,10 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
/* The writer is remote. This ring-buffer is read-only */
atomic_inc(&buffer->record_disabled);
nr_pages = desc->nr_page_va - 1;
+
+ if (nr_pages > rb_static_max_pages())
+ goto fail_free_buffers;
+
if (nr_pages < 2)
goto fail_free_buffers;
} else {
@@ -7839,6 +7856,9 @@ int ring_buffer_map(struct trace_buffer *buffer, int cpu,
/* prevent another thread from changing buffer/sub-buffer sizes */
guard(mutex)(&buffer->mutex);
+ if (cpu_buffer->nr_pages > rb_static_max_pages())
+ return -E2BIG;
+
err = rb_alloc_meta_page(cpu_buffer);
if (err)
return err;
--
2.55.0.860.g4b6b3295ed-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v8 3/3] ring-buffer: Prevent truncation of nr_pages / nr_subbufs
2026-08-26 9:45 [PATCH v8 0/3] ring-buffer: Fixes for subbuf resizing and persistent buffers Vincent Donnefort
2026-08-26 9:45 ` [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers Vincent Donnefort
2026-08-26 9:45 ` [PATCH v8 2/3] ring-buffer: Cap static ring buffer nr_pages Vincent Donnefort
@ 2026-08-26 9:45 ` Vincent Donnefort
2026-08-26 10:02 ` sashiko-bot
2 siblings, 1 reply; 15+ messages in thread
From: Vincent Donnefort @ 2026-08-26 9:45 UTC (permalink / raw)
To: rostedt, mhiramat, linux-trace-kernel
Cc: mathieu.desnoyers, kernel-team, linux-kernel, Vincent Donnefort
Although ring_buffer_per_cpu::nr_pages is defined as unsigned long, it
is capped to 32-bits in a few places, limiting the operations possible
on a very large buffer. Use `unsigned long` where appropriate and
prevent truncation of values using nr_pages (or nr_subbufs).
While at it, subbuf_size must be at least `unsigned int`.
Note that persistent, remote and user-mapped ring buffers are capping
the number of pages to 31 bits already, making "int" safe in many
places.
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 6089fcc67e2b..1fcf55e2ca81 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -1683,7 +1683,7 @@ static void rb_check_pages(struct ring_buffer_per_cpu *cpu_buffer)
* This is used to help find the next per cpu subbuffer within a mapped range.
*/
static unsigned long
-rb_range_align_subbuf(unsigned long addr, int subbuf_size, int nr_subbufs)
+rb_range_align_subbuf(unsigned long addr, unsigned int subbuf_size, unsigned long nr_subbufs)
{
addr += sizeof(struct ring_buffer_cpu_meta) +
sizeof(int) * nr_subbufs;
@@ -1693,13 +1693,12 @@ rb_range_align_subbuf(unsigned long addr, int subbuf_size, int nr_subbufs)
/*
* Return the ring_buffer_meta for a given @cpu.
*/
-static void *rb_range_meta(struct trace_buffer *buffer, int nr_pages, int cpu)
+static void *rb_range_meta(struct trace_buffer *buffer, unsigned long nr_pages, int cpu)
{
- int subbuf_size = rb_subbuf_size(buffer);
+ unsigned int subbuf_size = rb_subbuf_size(buffer);
struct ring_buffer_cpu_meta *meta;
struct ring_buffer_meta *bmeta;
- unsigned long ptr;
- int nr_subbufs;
+ unsigned long ptr, nr_subbufs;
bmeta = buffer->meta;
if (!bmeta)
@@ -1745,7 +1744,7 @@ static void *rb_range_meta(struct trace_buffer *buffer, int nr_pages, int cpu)
/* Return the start of subbufs given the meta pointer */
static void *rb_subbufs_from_meta(struct ring_buffer_cpu_meta *meta)
{
- int subbuf_size = meta->subbuf_size;
+ unsigned int subbuf_size = meta->subbuf_size;
unsigned long ptr;
ptr = (unsigned long)meta;
@@ -1757,11 +1756,11 @@ static void *rb_subbufs_from_meta(struct ring_buffer_cpu_meta *meta)
/*
* Return a specific sub-buffer for a given @cpu defined by @idx.
*/
-static void *rb_range_buffer(struct ring_buffer_per_cpu *cpu_buffer, int idx)
+static void *rb_range_buffer(struct ring_buffer_per_cpu *cpu_buffer, unsigned int idx)
{
struct ring_buffer_cpu_meta *meta;
+ unsigned int subbuf_size;
unsigned long ptr;
- int subbuf_size;
meta = rb_range_meta(cpu_buffer->buffer, 0, cpu_buffer->cpu);
if (!meta)
@@ -1777,7 +1776,7 @@ static void *rb_range_buffer(struct ring_buffer_per_cpu *cpu_buffer, int idx)
ptr = (unsigned long)rb_subbufs_from_meta(meta);
- ptr += subbuf_size * idx;
+ ptr += (unsigned long)subbuf_size * idx;
if (ptr + subbuf_size > cpu_buffer->buffer->range_addr_end)
return NULL;
@@ -1854,13 +1853,13 @@ static bool rb_meta_init(struct trace_buffer *buffer, int scratch_size)
* must be the same.
*/
static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
- struct trace_buffer *buffer, int nr_pages,
+ struct trace_buffer *buffer, unsigned long nr_pages,
unsigned long *subbuf_mask)
{
- int subbuf_size = PAGE_SIZE;
+ unsigned long subbuf_size = PAGE_SIZE;
unsigned long buffers_start;
unsigned long buffers_end;
- int i;
+ unsigned long i;
if (!subbuf_mask)
return false;
@@ -2109,8 +2108,8 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
struct buffer_page *head_page, *orig_head, *orig_reader;
struct rb_validation_state state = { 0 };
bool skip = false;
+ unsigned long i;
int ret;
- int i;
if (!meta || !meta->head_buffer)
return;
@@ -2161,7 +2160,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
rb_validate_buffer(head_page, cpu_buffer, meta, &state, 0, state.ts);
}
if (i)
- pr_info("Ring buffer [%d] rewound %d pages\n", cpu_buffer->cpu, i);
+ pr_info("Ring buffer [%d] rewound %lu pages\n", cpu_buffer->cpu, i);
/* The last rewound page must be skipped. */
if (head_page != orig_head)
@@ -2245,7 +2244,8 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
}
}
-static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages, int scratch_size)
+static void rb_range_meta_init(struct trace_buffer *buffer,
+ unsigned long nr_pages, int scratch_size)
{
struct ring_buffer_cpu_meta *meta;
unsigned long *subbuf_mask;
@@ -2345,8 +2345,8 @@ static int rbm_show(struct seq_file *m, void *v)
rb_meta_subbuf_idx(meta, (void *)meta->head_buffer));
seq_printf(m, "commit_buffer: %d\n",
rb_meta_subbuf_idx(meta, (void *)meta->commit_buffer));
- seq_printf(m, "subbuf_size: %d\n", meta->subbuf_size);
- seq_printf(m, "nr_subbufs: %d\n", meta->nr_subbufs);
+ seq_printf(m, "subbuf_size: %u\n", meta->subbuf_size);
+ seq_printf(m, "nr_subbufs: %u\n", meta->nr_subbufs);
return 0;
}
@@ -2431,7 +2431,7 @@ static void *ring_buffer_desc_page(struct ring_buffer_desc *desc, unsigned int p
}
static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
- long nr_pages, struct list_head *pages)
+ unsigned long nr_pages, struct list_head *pages)
{
struct trace_buffer *buffer = cpu_buffer->buffer;
struct ring_buffer_cpu_meta *meta = NULL;
@@ -2559,7 +2559,7 @@ static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
}
static struct ring_buffer_per_cpu *
-rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu)
+rb_allocate_cpu_buffer(struct trace_buffer *buffer, unsigned long nr_pages, int cpu)
{
struct ring_buffer_per_cpu *cpu_buffer __free(kfree) =
alloc_cpu_buffer(cpu);
@@ -2716,8 +2716,8 @@ static void rb_test_inject_invalid_pages(struct trace_buffer *buffer)
struct ring_buffer_cpu_meta *meta;
struct buffer_data_page *dpage;
unsigned long entry_bytes = 0;
+ unsigned int subbuf_size;
unsigned long ptr;
- int subbuf_size;
int invalid = 0;
int cpu;
int i;
@@ -2787,8 +2787,8 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
struct ring_buffer_remote *remote)
{
struct trace_buffer *buffer __free(kfree) = NULL;
- long nr_pages;
- int subbuf_size;
+ unsigned int subbuf_size;
+ unsigned long nr_pages;
int bsize;
int cpu;
int ret;
@@ -5872,12 +5872,12 @@ __rb_get_reader_page_from_remote(struct ring_buffer_per_cpu *cpu_buffer)
static struct buffer_page *
__rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
{
- int max_loops = cpu_buffer->ring_meta ? cpu_buffer->nr_pages : 3;
+ unsigned long max_loops = cpu_buffer->ring_meta ? cpu_buffer->nr_pages : 3;
struct buffer_page *reader = NULL;
+ unsigned long nr_loops = 0;
unsigned long overwrite;
unsigned long flags;
int missed_events = 0;
- int nr_loops = 0;
bool ret;
local_irq_save(flags);
@@ -6195,8 +6195,8 @@ rb_iter_peek(struct ring_buffer_iter *iter, u64 *ts)
struct trace_buffer *buffer;
struct ring_buffer_per_cpu *cpu_buffer;
struct ring_buffer_event *event;
- int nr_loops = 0;
- int max_loops;
+ unsigned long nr_loops = 0;
+ unsigned long max_loops;
if (ts)
*ts = 0;
@@ -7442,8 +7442,8 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
struct ring_buffer_per_cpu *cpu_buffer;
struct buffer_page *bpage, *tmp;
unsigned int old_capacity;
+ unsigned long nr_pages;
int old_order;
- int nr_pages;
int psize;
int err;
int cpu;
@@ -7625,10 +7625,10 @@ static void rb_setup_ids_meta_page(struct ring_buffer_per_cpu *cpu_buffer,
struct buffer_page **subbuf_ids)
{
struct trace_buffer_meta *meta = cpu_buffer->meta_page;
- unsigned int nr_subbufs = cpu_buffer->nr_pages + 1;
+ unsigned long nr_subbufs = cpu_buffer->nr_pages + 1;
struct buffer_page *first_subbuf, *subbuf;
- int cnt = 0;
- int id = 0;
+ unsigned int cnt = 0;
+ unsigned int id = 0;
id = rb_page_id(cpu_buffer, cpu_buffer->reader_page, id);
subbuf_ids[id++] = cpu_buffer->reader_page;
--
2.55.0.860.g4b6b3295ed-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers
2026-08-26 9:45 ` [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers Vincent Donnefort
@ 2026-08-26 9:59 ` sashiko-bot
2026-08-26 14:37 ` Steven Rostedt
0 siblings, 1 reply; 15+ messages in thread
From: sashiko-bot @ 2026-08-26 9:59 UTC (permalink / raw)
To: Vincent Donnefort; +Cc: linux-trace-kernel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] ring_buffer_read_page() incorrectly returns -1 for splice_read requests on subbuf order mismatch
--
commit c4d6ee6f55f23c8ec6da6f812ec3b722b2b7f6b6
Author: Vincent Donnefort <vdonnefort@google.com>
tracing: Fix subbuf resize races with trace_pipe_raw readers
This commit modifies ring_buffer_alloc_read_page() and
ring_buffer_read_page() to handle dynamic resizing of trace ring
buffers, allowing readers to avoid stale size values. It introduces
a manual data copy fallback in ring_buffer_read_page() when the
subbuffer orders do not match.
> diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> --- a/kernel/trace/ring_buffer.c
> +++ b/kernel/trace/ring_buffer.c
[ ... ]
> @@ -7183,7 +7218,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
> */
> if (read || (len < (size - read)) ||
> cpu_buffer->reader_page == cpu_buffer->commit_page ||
> - rb_is_static(cpu_buffer)) {
> + rb_is_static(cpu_buffer) ||
> + data_page->order != reader->order) {
> struct buffer_data_page *rpage = cpu_buffer->reader_page->page;
> unsigned int rpos = read;
> unsigned int pos = 0;
[Severity: High]
Does this new condition break splice reads when the buffer order changes?
When tracing_buffers_splice_read() allocates a read page with the new order
and calls ring_buffer_read_page() with full=1, it will now enter this manual
copy block because the subbuffer orders no longer match.
However, just a few lines down inside this block in ring_buffer_read_page(),
there is the following check:
if (full &&
(!read || (len < (size - read)) ||
cpu_buffer->reader_page == cpu_buffer->commit_page))
return -1;
Since the reader page typically hasn't been partially read yet (read == 0),
and full == 1 from splice, this check evaluates to true and immediately
returns -1 instead of copying the data.
Because tracing_buffers_splice_read() loops and ultimately returns -EAGAIN
on failure, will this cause an infinite loop of -EAGAIN for userspace tools
trying to read across buffer resizes?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260826094528.3738023-1-vdonnefort@google.com?part=1
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 3/3] ring-buffer: Prevent truncation of nr_pages / nr_subbufs
2026-08-26 9:45 ` [PATCH v8 3/3] ring-buffer: Prevent truncation of nr_pages / nr_subbufs Vincent Donnefort
@ 2026-08-26 10:02 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-08-26 10:02 UTC (permalink / raw)
To: Vincent Donnefort; +Cc: linux-trace-kernel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The kernel fails to validate the untrusted `meta->nr_subbufs` value from persistent memory against the expected `nr_pages + 1`, leading to arbitrary heap out-of-bounds writes and potential arbitrary kernel memory corruption.
--
commit 5ed0ab549782b2ff87e1bd2394abb78b10c4b602
Author: Vincent Donnefort <vdonnefort@google.com>
ring-buffer: Prevent truncation of nr_pages / nr_subbufs
Although ring_buffer_per_cpu::nr_pages is defined as unsigned long, it
is capped to 32-bits in a few places, limiting the operations possible
on a very large buffer. Use unsigned long where appropriate and
prevent truncation of values using nr_pages (or nr_subbufs).
While at it, subbuf_size must be at least unsigned int.
Note that persistent, remote and user-mapped ring buffers are capping
the number of pages to 31 bits already, making "int" safe in many
places.
> diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> index 6089fcc67e2b6..1fcf55e2ca81f 100644
> --- a/kernel/trace/ring_buffer.c
> +++ b/kernel/trace/ring_buffer.c
> @@ -1745,7 +1744,7 @@ static void *rb_range_meta(struct trace_buffer *buffer, int nr_pages, int cpu)
> /* Return the start of subbufs given the meta pointer */
> static void *rb_subbufs_from_meta(struct ring_buffer_cpu_meta *meta)
> {
> - int subbuf_size = meta->subbuf_size;
> + unsigned int subbuf_size = meta->subbuf_size;
> unsigned long ptr;
>
> ptr = (unsigned long)meta;
[Severity: High]
This isn't a bug introduced by this patch, but does the next line in this
function perform pointer arithmetic using an unvalidated meta->nr_subbufs?
ptr = rb_range_align_subbuf(ptr, subbuf_size, meta->nr_subbufs);
Could this yield a corrupted or attacker-controlled subbuf base pointer if
meta->nr_subbufs is read from persistent memory without being checked against
expected limits?
[ ... ]
> @@ -1854,13 +1853,13 @@ static bool rb_meta_init(struct trace_buffer *buffer, int scratch_size)
> * must be the same.
> */
> static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
> - struct trace_buffer *buffer, int nr_pages,
> + struct trace_buffer *buffer, unsigned long nr_pages,
> unsigned long *subbuf_mask)
> {
> - int subbuf_size = PAGE_SIZE;
> + unsigned long subbuf_size = PAGE_SIZE;
> unsigned long buffers_start;
> unsigned long buffers_end;
> - int i;
> + unsigned long i;
>
> if (!subbuf_mask)
> return false;
[Severity: High]
This isn't a bug introduced by this patch, but is there a risk of an
out-of-bounds heap write further down in this function?
The subbuf_mask is allocated with nr_pages + 1 bits in rb_range_meta_init(),
but later in rb_cpu_meta_valid() operations use the unvalidated
meta->nr_subbufs value read from persistent memory:
bitmap_clear(subbuf_mask, 0, meta->nr_subbufs);
/*
* Ensure the meta::buffers array has correct data. The data in each subbufs
* are checked later in rb_meta_validate_events().
*/
for (i = 0; i < meta->nr_subbufs; i++) {
if (meta->buffers[i] < 0 ||
meta->buffers[i] >= meta->nr_subbufs) {
pr_info("Ring buffer boot meta [%d] array out of range\n", cpu);
return false;
}
if (test_bit(meta->buffers[i], subbuf_mask)) {
pr_info("Ring buffer boot meta [%d] array has duplicates\n", cpu);
return false;
}
set_bit(meta->buffers[i], subbuf_mask);
}
If meta->nr_subbufs is larger than nr_pages + 1, could this write out of
bounds and corrupt kernel memory?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260826094528.3738023-1-vdonnefort@google.com?part=3
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers
2026-08-26 9:59 ` sashiko-bot
@ 2026-08-26 14:37 ` Steven Rostedt
2026-08-26 16:24 ` Vincent Donnefort
0 siblings, 1 reply; 15+ messages in thread
From: Steven Rostedt @ 2026-08-26 14:37 UTC (permalink / raw)
To: sashiko-bot; +Cc: sashiko-reviews, Vincent Donnefort, linux-trace-kernel
On Wed, 26 Aug 2026 09:59:46 +0000
sashiko-bot@kernel.org wrote:
> > diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> > --- a/kernel/trace/ring_buffer.c
> > +++ b/kernel/trace/ring_buffer.c
> [ ... ]
> > @@ -7183,7 +7218,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
> > */
> > if (read || (len < (size - read)) ||
> > cpu_buffer->reader_page == cpu_buffer->commit_page ||
> > - rb_is_static(cpu_buffer)) {
> > + rb_is_static(cpu_buffer) ||
> > + data_page->order != reader->order) {
> > struct buffer_data_page *rpage = cpu_buffer->reader_page->page;
> > unsigned int rpos = read;
> > unsigned int pos = 0;
>
> [Severity: High]
> Does this new condition break splice reads when the buffer order changes?
>
> When tracing_buffers_splice_read() allocates a read page with the new order
> and calls ring_buffer_read_page() with full=1, it will now enter this manual
> copy block because the subbuffer orders no longer match.
>
> However, just a few lines down inside this block in ring_buffer_read_page(),
> there is the following check:
>
> if (full &&
> (!read || (len < (size - read)) ||
> cpu_buffer->reader_page == cpu_buffer->commit_page))
> return -1;
>
> Since the reader page typically hasn't been partially read yet (read == 0),
> and full == 1 from splice, this check evaluates to true and immediately
> returns -1 instead of copying the data.
>
> Because tracing_buffers_splice_read() loops and ultimately returns -EAGAIN
> on failure, will this cause an infinite loop of -EAGAIN for userspace tools
> trying to read across buffer resizes?
>
This is a legitimate concern. But with an easy solution. Perhaps just add:
if (full &&
(!read || (len < (size - read)) ||
- cpu_buffer->reader_page == cpu_buffer->commit_page))
+ cpu_buffer->reader_page == cpu_buffer->commit_page) &&
+ data_page->order == buffer->subbuf_order)
return -1;
If the user is changing the buffer size at the same time as reading raw
pages, they get what they deserve! I just don't want to let the kernel go
into an infinite loop.
-- Steve
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers
2026-08-26 14:37 ` Steven Rostedt
@ 2026-08-26 16:24 ` Vincent Donnefort
2026-08-26 18:31 ` Steven Rostedt
0 siblings, 1 reply; 15+ messages in thread
From: Vincent Donnefort @ 2026-08-26 16:24 UTC (permalink / raw)
To: Steven Rostedt; +Cc: sashiko-bot, sashiko-reviews, linux-trace-kernel
On Wed, Aug 26, 2026 at 10:37:36AM -0400, Steven Rostedt wrote:
> On Wed, 26 Aug 2026 09:59:46 +0000
> sashiko-bot@kernel.org wrote:
>
> > > diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> > > --- a/kernel/trace/ring_buffer.c
> > > +++ b/kernel/trace/ring_buffer.c
> > [ ... ]
> > > @@ -7183,7 +7218,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
> > > */
> > > if (read || (len < (size - read)) ||
> > > cpu_buffer->reader_page == cpu_buffer->commit_page ||
> > > - rb_is_static(cpu_buffer)) {
> > > + rb_is_static(cpu_buffer) ||
> > > + data_page->order != reader->order) {
> > > struct buffer_data_page *rpage = cpu_buffer->reader_page->page;
> > > unsigned int rpos = read;
> > > unsigned int pos = 0;
> >
> > [Severity: High]
> > Does this new condition break splice reads when the buffer order changes?
> >
> > When tracing_buffers_splice_read() allocates a read page with the new order
> > and calls ring_buffer_read_page() with full=1, it will now enter this manual
> > copy block because the subbuffer orders no longer match.
> >
> > However, just a few lines down inside this block in ring_buffer_read_page(),
> > there is the following check:
> >
> > if (full &&
> > (!read || (len < (size - read)) ||
> > cpu_buffer->reader_page == cpu_buffer->commit_page))
> > return -1;
> >
> > Since the reader page typically hasn't been partially read yet (read == 0),
> > and full == 1 from splice, this check evaluates to true and immediately
> > returns -1 instead of copying the data.
> >
> > Because tracing_buffers_splice_read() loops and ultimately returns -EAGAIN
> > on failure, will this cause an infinite loop of -EAGAIN for userspace tools
> > trying to read across buffer resizes?
> >
>
>
> This is a legitimate concern. But with an easy solution. Perhaps just add:
>
> if (full &&
> (!read || (len < (size - read)) ||
> - cpu_buffer->reader_page == cpu_buffer->commit_page))
> + cpu_buffer->reader_page == cpu_buffer->commit_page) &&
> + data_page->order == buffer->subbuf_order)
> return -1;
>
> If the user is changing the buffer size at the same time as reading raw
> pages, they get what they deserve! I just don't want to let the kernel go
> into an infinite loop.
>
> -- Steve
>
Sounds good. Do you prefer to get a v9 or you fold this into the existing commit?
--
Vincent
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers
2026-08-26 16:24 ` Vincent Donnefort
@ 2026-08-26 18:31 ` Steven Rostedt
2026-08-27 6:31 ` Vincent Donnefort
0 siblings, 1 reply; 15+ messages in thread
From: Steven Rostedt @ 2026-08-26 18:31 UTC (permalink / raw)
To: Vincent Donnefort; +Cc: sashiko-bot, sashiko-reviews, linux-trace-kernel
On Wed, 26 Aug 2026 17:24:35 +0100
Vincent Donnefort <vdonnefort@google.com> wrote:
> > This is a legitimate concern. But with an easy solution. Perhaps just add:
> >
> > if (full &&
> > (!read || (len < (size - read)) ||
> > - cpu_buffer->reader_page == cpu_buffer->commit_page))
> > + cpu_buffer->reader_page == cpu_buffer->commit_page) &&
> > + data_page->order == buffer->subbuf_order)
> > return -1;
> >
> > If the user is changing the buffer size at the same time as reading raw
> > pages, they get what they deserve! I just don't want to let the kernel go
> > into an infinite loop.
> >
> > -- Steve
> >
>
> Sounds good. Do you prefer to get a v9 or you fold this into the existing commit?
>
Please send a v9. It makes the patchwork flow much better. If I modify a
patch, it makes patchwork not be able to automate the status updates as it
moves around git trees.
Thanks,
-- Steve
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers
2026-08-26 18:31 ` Steven Rostedt
@ 2026-08-27 6:31 ` Vincent Donnefort
2026-08-27 13:15 ` Steven Rostedt
0 siblings, 1 reply; 15+ messages in thread
From: Vincent Donnefort @ 2026-08-27 6:31 UTC (permalink / raw)
To: Steven Rostedt; +Cc: sashiko-bot, sashiko-reviews, linux-trace-kernel
On Wed, Aug 26, 2026 at 02:31:23PM -0400, Steven Rostedt wrote:
> On Wed, 26 Aug 2026 17:24:35 +0100
> Vincent Donnefort <vdonnefort@google.com> wrote:
>
> > > This is a legitimate concern. But with an easy solution. Perhaps just add:
> > >
> > > if (full &&
> > > (!read || (len < (size - read)) ||
> > > - cpu_buffer->reader_page == cpu_buffer->commit_page))
> > > + cpu_buffer->reader_page == cpu_buffer->commit_page) &&
> > > + data_page->order == buffer->subbuf_order)
> > > return -1;
> > >
> > > If the user is changing the buffer size at the same time as reading raw
> > > pages, they get what they deserve! I just don't want to let the kernel go
> > > into an infinite loop.
> > >
> > > -- Steve
> > >
> >
> > Sounds good. Do you prefer to get a v9 or you fold this into the existing commit?
> >
>
> Please send a v9. It makes the patchwork flow much better. If I modify a
> patch, it makes patchwork not be able to automate the status updates as it
> moves around git trees.
>
> Thanks,
>
> -- Steve
>
Looking at the function again, shouldn't we add !rb_is_static(cpu_buffer) as
well to the full page test?
--
Vincent
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers
2026-08-27 6:31 ` Vincent Donnefort
@ 2026-08-27 13:15 ` Steven Rostedt
2026-08-27 16:21 ` Vincent Donnefort
0 siblings, 1 reply; 15+ messages in thread
From: Steven Rostedt @ 2026-08-27 13:15 UTC (permalink / raw)
To: Vincent Donnefort; +Cc: sashiko-bot, sashiko-reviews, linux-trace-kernel
On Thu, 27 Aug 2026 07:31:23 +0100
Vincent Donnefort <vdonnefort@google.com> wrote:
> Looking at the function again, shouldn't we add !rb_is_static(cpu_buffer) as
> well to the full page test?
Why?
-- Steve
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers
2026-08-27 13:15 ` Steven Rostedt
@ 2026-08-27 16:21 ` Vincent Donnefort
2026-08-27 19:33 ` Steven Rostedt
0 siblings, 1 reply; 15+ messages in thread
From: Vincent Donnefort @ 2026-08-27 16:21 UTC (permalink / raw)
To: Steven Rostedt; +Cc: sashiko-bot, sashiko-reviews, linux-trace-kernel
On Thu, Aug 27, 2026 at 09:15:41AM -0400, Steven Rostedt wrote:
> On Thu, 27 Aug 2026 07:31:23 +0100
> Vincent Donnefort <vdonnefort@google.com> wrote:
>
> > Looking at the function again, shouldn't we add !rb_is_static(cpu_buffer) as
> > well to the full page test?
>
> Why?
>
> -- Steve
So that the splice can make progress when the ring-buffer is presistent or
user-mapped. Or do we say that's expected behaviour?
--
Vincent
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers
2026-08-27 16:21 ` Vincent Donnefort
@ 2026-08-27 19:33 ` Steven Rostedt
2026-08-28 8:24 ` Vincent Donnefort
0 siblings, 1 reply; 15+ messages in thread
From: Steven Rostedt @ 2026-08-27 19:33 UTC (permalink / raw)
To: Vincent Donnefort; +Cc: sashiko-bot, sashiko-reviews, linux-trace-kernel
On Thu, 27 Aug 2026 17:21:39 +0100
Vincent Donnefort <vdonnefort@google.com> wrote:
> On Thu, Aug 27, 2026 at 09:15:41AM -0400, Steven Rostedt wrote:
> > On Thu, 27 Aug 2026 07:31:23 +0100
> > Vincent Donnefort <vdonnefort@google.com> wrote:
> >
> > > Looking at the function again, shouldn't we add !rb_is_static(cpu_buffer) as
> > > well to the full page test?
> >
> > Why?
> >
> > -- Steve
>
> So that the splice can make progress when the ring-buffer is presistent or
> user-mapped. Or do we say that's expected behaviour?
>
Ah yeah, I see. Hmm, looking at the code to see how this would work, I
think I found another bug. Let me look deeper into it.
-- Steve
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers
2026-08-27 19:33 ` Steven Rostedt
@ 2026-08-28 8:24 ` Vincent Donnefort
2026-08-28 8:36 ` Steven Rostedt
0 siblings, 1 reply; 15+ messages in thread
From: Vincent Donnefort @ 2026-08-28 8:24 UTC (permalink / raw)
To: Steven Rostedt; +Cc: sashiko-bot, sashiko-reviews, linux-trace-kernel
On Thu, Aug 27, 2026 at 03:33:14PM -0400, Steven Rostedt wrote:
> On Thu, 27 Aug 2026 17:21:39 +0100
> Vincent Donnefort <vdonnefort@google.com> wrote:
>
> > On Thu, Aug 27, 2026 at 09:15:41AM -0400, Steven Rostedt wrote:
> > > On Thu, 27 Aug 2026 07:31:23 +0100
> > > Vincent Donnefort <vdonnefort@google.com> wrote:
> > >
> > > > Looking at the function again, shouldn't we add !rb_is_static(cpu_buffer) as
> > > > well to the full page test?
> > >
> > > Why?
> > >
> > > -- Steve
> >
> > So that the splice can make progress when the ring-buffer is presistent or
> > user-mapped. Or do we say that's expected behaviour?
> >
>
> Ah yeah, I see. Hmm, looking at the code to see how this would work, I
> think I found another bug. Let me look deeper into it.
>
> -- Steve
A bug if we allow rb_is_static()?
If not, shall I add it to my series?
--
Vincent
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers
2026-08-28 8:24 ` Vincent Donnefort
@ 2026-08-28 8:36 ` Steven Rostedt
0 siblings, 0 replies; 15+ messages in thread
From: Steven Rostedt @ 2026-08-28 8:36 UTC (permalink / raw)
To: Vincent Donnefort; +Cc: sashiko-bot, sashiko-reviews, linux-trace-kernel
On August 28, 2026 4:24:36 AM EDT, Vincent Donnefort <vdonnefort@google.com> wrote:
>On Thu, Aug 27, 2026 at 03:33:14PM -0400, Steven Rostedt wrote:
>> On Thu, 27 Aug 2026 17:21:39 +0100
>> Vincent Donnefort <vdonnefort@google.com> wrote:
>>
>> > On Thu, Aug 27, 2026 at 09:15:41AM -0400, Steven Rostedt wrote:
>> > > On Thu, 27 Aug 2026 07:31:23 +0100
>> > > Vincent Donnefort <vdonnefort@google.com> wrote:
>> > >
>> > > > Looking at the function again, shouldn't we add !rb_is_static(cpu_buffer) as
>> > > > well to the full page test?
>> > >
>> > > Why?
>> > >
>> > > -- Steve
>> >
>> > So that the splice can make progress when the ring-buffer is presistent or
>> > user-mapped. Or do we say that's expected behaviour?
>> >
>>
>> Ah yeah, I see. Hmm, looking at the code to see how this would work, I
>> think I found another bug. Let me look deeper into it.
>>
>> -- Steve
>
>A bug if we allow rb_is_static()?
No, something else.
>
>If not, shall I add it to my series?
Yeah, go ahead and update it.
Thanks,
-- Steve
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-08-28 8:36 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-26 9:45 [PATCH v8 0/3] ring-buffer: Fixes for subbuf resizing and persistent buffers Vincent Donnefort
2026-08-26 9:45 ` [PATCH v8 1/3] tracing: Fix subbuf resize races with trace_pipe_raw readers Vincent Donnefort
2026-08-26 9:59 ` sashiko-bot
2026-08-26 14:37 ` Steven Rostedt
2026-08-26 16:24 ` Vincent Donnefort
2026-08-26 18:31 ` Steven Rostedt
2026-08-27 6:31 ` Vincent Donnefort
2026-08-27 13:15 ` Steven Rostedt
2026-08-27 16:21 ` Vincent Donnefort
2026-08-27 19:33 ` Steven Rostedt
2026-08-28 8:24 ` Vincent Donnefort
2026-08-28 8:36 ` Steven Rostedt
2026-08-26 9:45 ` [PATCH v8 2/3] ring-buffer: Cap static ring buffer nr_pages Vincent Donnefort
2026-08-26 9:45 ` [PATCH v8 3/3] ring-buffer: Prevent truncation of nr_pages / nr_subbufs Vincent Donnefort
2026-08-26 10:02 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox