When calling ftrace_dump_one() concurrently with reading trace_pipe,
a WARN_ON_ONCE() in trace_printk_seq() can be triggered due to a race
condition.
The issue occurs because:
CPU0 (ftrace_dump) CPU1 (reader)
echo z > /proc/sysrq-trigger
!trace_empty(&iter)
trace_iterator_reset(&iter) <- len = size = 0
cat /sys/kernel/tracing/trace_pipe
trace_find_next_entry_inc(&iter)
__find_next_entry
ring_buffer_empty_cpu <- all empty
return NULL
trace_printk_seq(&iter.seq)
WARN_ON_ONCE(s->seq.len >= s->seq.size)
In the context between trace_empty() and trace_find_next_entry_inc()
during ftrace_dump, the ring buffer data was consumed by other readers.
This caused trace_find_next_entry_inc to return NULL, failing to populate
`iter.seq`. At this point, due to the prior trace_iterator_reset, both
`iter.seq.len` and `iter.seq.size` were set to 0. Since they are equal,
the WARN_ON_ONCE condition is triggered.
Add a non-NULL check on the return value of trace_find_next_entry_inc
prior to invoking trace_printk_seq, ensuring the `iter.seq` is properly
populated before subsequent operations.
Furthermore, per the seq_buf specification, the condition len == size
indicates a full buffer, which constitutes a valid state. Consequently,
the equality check and size - 1 adjustment in WARN_ON_ONCE() are redundant
and should be eliminated.
Fixes: d769041f8653 ("ring_buffer: implement new locking")
Signed-off-by: Tengda Wu <redacted>
---
kernel/trace/trace.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
@@ -10521,8 +10521,8 @@ trace_printk_seq(struct trace_seq *s)*PAGE_SIZE,andTRACE_MAX_PRINTis1000,thisisjust*anextralayerofprotection.*/-if(WARN_ON_ONCE(s->seq.len>=s->seq.size))-s->seq.len=s->seq.size-1;+if(WARN_ON_ONCE(s->seq.len>s->seq.size))+s->seq.len=s->seq.size;/* should be zero ended, but we are paranoid. */s->buffer[s->seq.len]=0;
From: Steven Rostedt <rostedt@goodmis.org> Date: 2025-08-20 14:10:56
On Wed, 20 Aug 2025 09:00:17 +0000
Tengda Wu [off-list ref] wrote:
Hi Tengda!
When calling ftrace_dump_one() concurrently with reading trace_pipe,
a WARN_ON_ONCE() in trace_printk_seq() can be triggered due to a race
condition.
The issue occurs because:
CPU0 (ftrace_dump) CPU1 (reader)
echo z > /proc/sysrq-trigger
!trace_empty(&iter)
trace_iterator_reset(&iter) <- len = size = 0
cat /sys/kernel/tracing/trace_pipe
trace_find_next_entry_inc(&iter)
__find_next_entry
ring_buffer_empty_cpu <- all empty
return NULL
trace_printk_seq(&iter.seq)
WARN_ON_ONCE(s->seq.len >= s->seq.size)
Thanks for debugging this code. I do appreciate it.
In the context between trace_empty() and trace_find_next_entry_inc()
during ftrace_dump, the ring buffer data was consumed by other readers.
This caused trace_find_next_entry_inc to return NULL, failing to populate
`iter.seq`. At this point, due to the prior trace_iterator_reset, both
`iter.seq.len` and `iter.seq.size` were set to 0. Since they are equal,
the WARN_ON_ONCE condition is triggered.
Add a non-NULL check on the return value of trace_find_next_entry_inc
prior to invoking trace_printk_seq, ensuring the `iter.seq` is properly
populated before subsequent operations.
quoted hunk
Furthermore, per the seq_buf specification, the condition len == size
indicates a full buffer, which constitutes a valid state. Consequently,
the equality check and size - 1 adjustment in WARN_ON_ONCE() are redundant
and should be eliminated.
Fixes: d769041f8653 ("ring_buffer: implement new locking")
Signed-off-by: Tengda Wu <redacted>
---
kernel/trace/trace.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
This should be a separate patch as it has nothing to do with the other
changes. It's also incorrect, which is why you want to make it a separate
patch, as now this delays the changes below from being accepted.
Yes, the seq_buf specification states this, but this is not using the
seq_buf interface. It's adding on to it. The code below that change has:
/* should be zero ended, but we are paranoid. */
s->buffer[s->seq.len] = 0;
You see, it is updating the buffer. Which means it needs its own check.
If we don't set len to size - 1, the above write will overflow the buffer.
-- Steve
quoted hunk
/* should be zero ended, but we are paranoid. */
s->buffer[s->seq.len] = 0;
On Wed, 20 Aug 2025 09:00:17 +0000
Tengda Wu [off-list ref] wrote:
Hi Tengda!
quoted
When calling ftrace_dump_one() concurrently with reading trace_pipe,
a WARN_ON_ONCE() in trace_printk_seq() can be triggered due to a race
condition.
The issue occurs because:
CPU0 (ftrace_dump) CPU1 (reader)
echo z > /proc/sysrq-trigger
!trace_empty(&iter)
trace_iterator_reset(&iter) <- len = size = 0
cat /sys/kernel/tracing/trace_pipe
trace_find_next_entry_inc(&iter)
__find_next_entry
ring_buffer_empty_cpu <- all empty
return NULL
trace_printk_seq(&iter.seq)
WARN_ON_ONCE(s->seq.len >= s->seq.size)
Thanks for debugging this code. I do appreciate it.
quoted
In the context between trace_empty() and trace_find_next_entry_inc()
during ftrace_dump, the ring buffer data was consumed by other readers.
This caused trace_find_next_entry_inc to return NULL, failing to populate
`iter.seq`. At this point, due to the prior trace_iterator_reset, both
`iter.seq.len` and `iter.seq.size` were set to 0. Since they are equal,
the WARN_ON_ONCE condition is triggered.
Add a non-NULL check on the return value of trace_find_next_entry_inc
prior to invoking trace_printk_seq, ensuring the `iter.seq` is properly
populated before subsequent operations.
quoted
Furthermore, per the seq_buf specification, the condition len == size
indicates a full buffer, which constitutes a valid state. Consequently,
the equality check and size - 1 adjustment in WARN_ON_ONCE() are redundant
and should be eliminated.
Fixes: d769041f8653 ("ring_buffer: implement new locking")
Signed-off-by: Tengda Wu <redacted>
---
kernel/trace/trace.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
This should be a separate patch as it has nothing to do with the other
changes. It's also incorrect, which is why you want to make it a separate
patch, as now this delays the changes below from being accepted.
Understood. I'll split it off right away and send a v2 with only the
relevant changes.
Yes, the seq_buf specification states this, but this is not using the
seq_buf interface. It's adding on to it. The code below that change has:
/* should be zero ended, but we are paranoid. */
s->buffer[s->seq.len] = 0;
You see, it is updating the buffer. Which means it needs its own check.
If we don't set len to size - 1, the above write will overflow the buffer.
-- Steve
Indeed, that was an error on my part. The len field should not account for
the null terminator. Thank you for the correction.
There remains an edge case that concerns me: if size is 0, setting len to
size - 1 would cause an underflow. Should we handle this edge case?
-- Tengda
quoted
/* should be zero ended, but we are paranoid. */
s->buffer[s->seq.len] = 0;
From: Steven Rostedt <rostedt@goodmis.org> Date: 2025-08-21 14:51:13
On Thu, 21 Aug 2025 09:53:53 +0800
Tengda Wu [off-list ref] wrote:
There remains an edge case that concerns me: if size is 0, setting len to
size - 1 would cause an underflow. Should we handle this edge case?
When the trace_seq is allocated, the size is set to 8K. If size is ever
zero, then there would be no buffer. So no, we should not worry about that
edge case because if it happened then we have a lot of other things to
worry about.
-- Steve
On Thu, 21 Aug 2025 09:53:53 +0800
Tengda Wu [off-list ref] wrote:
quoted
There remains an edge case that concerns me: if size is 0, setting len to
size - 1 would cause an underflow. Should we handle this edge case?
When the trace_seq is allocated, the size is set to 8K. If size is ever
zero, then there would be no buffer. So no, we should not worry about that
edge case because if it happened then we have a lot of other things to
worry about.
-- Steve