Linux Trace Kernel
 help / color / mirror / Atom feed
* [PATCH] tracing: Use seq_buf for string concatenation
@ 2026-06-20 17:54 Woradorn Laodhanadhaworn
  2026-06-21 17:16 ` Jori Koolstra
  2026-06-22  5:20 ` XIAO WU
  0 siblings, 2 replies; 4+ messages in thread
From: Woradorn Laodhanadhaworn @ 2026-06-20 17:54 UTC (permalink / raw)
  To: rostedt
  Cc: mhiramat, mathieu.desnoyers, linux-kernel, linux-trace-kernel,
	linux-hardening, linux-kernel-mentees, shuah, skhan, me,
	jkoolstra, woradorn.laon

In preparation for removing the strlcat API[1],
replace the string concatenation logic with a struct seq_buf,
which tracks the current position and the remaining space internally.

The backing buffer bootup_event_buf allocation is unchanged.
Use seq_buf_str() to NUL-terminate before passing to early_enable_events().

Link: https://github.com/KSPP/linux/issues/370 [1]

Signed-off-by: Woradorn Laodhanadhaworn <woradorn.laon@gmail.com>
---
 kernel/trace/trace_events.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index c46e623e7e0d..15164723e028 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -22,6 +22,7 @@
 #include <linux/sort.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
+#include <linux/seq_buf.h>
 
 #include <trace/events/sched.h>
 #include <trace/syscall.h>
@@ -4501,13 +4502,23 @@ extern struct trace_event_call *__start_ftrace_events[];
 extern struct trace_event_call *__stop_ftrace_events[];
 
 static char bootup_event_buf[COMMAND_LINE_SIZE] __initdata;
+static struct seq_buf bootup_event_seq;
+static bool bootup_event_seq_initialized;
 
 static __init int setup_trace_event(char *str)
 {
-	if (bootup_event_buf[0] != '\0')
-		strlcat(bootup_event_buf, ",", COMMAND_LINE_SIZE);
+	if (!bootup_event_seq_initialized) {
+		seq_buf_init(&bootup_event_seq, bootup_event_buf, COMMAND_LINE_SIZE);
+		bootup_event_seq_initialized = true;
+	}
+
+	if (seq_buf_used(&bootup_event_seq) > 0)
+		seq_buf_puts(&bootup_event_seq, ",");
 
-	strlcat(bootup_event_buf, str, COMMAND_LINE_SIZE);
+	seq_buf_puts(&bootup_event_seq, str);
+
+	if (seq_buf_has_overflowed(&bootup_event_seq))
+		return -ENOMEM;
 
 	trace_set_ring_buffer_expanded(NULL);
 	disable_tracing_selftest("running event tracing");
@@ -4766,7 +4777,7 @@ static __init int event_trace_enable(void)
 	 */
 	__trace_early_add_events(tr);
 
-	early_enable_events(tr, bootup_event_buf, false);
+	early_enable_events(tr, (char *)seq_buf_str(&bootup_event_seq), false);
 
 	trace_printk_start_comm();
 
@@ -4794,7 +4805,7 @@ static __init int event_trace_enable_again(void)
 	if (!tr)
 		return -ENODEV;
 
-	early_enable_events(tr, bootup_event_buf, true);
+	early_enable_events(tr, (char *)seq_buf_str(&bootup_event_seq), true);
 
 	return 0;
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] tracing: Use seq_buf for string concatenation
  2026-06-20 17:54 [PATCH] tracing: Use seq_buf for string concatenation Woradorn Laodhanadhaworn
@ 2026-06-21 17:16 ` Jori Koolstra
  2026-06-22  8:18   ` Woradorn Laodhanadhaworn
  2026-06-22  5:20 ` XIAO WU
  1 sibling, 1 reply; 4+ messages in thread
From: Jori Koolstra @ 2026-06-21 17:16 UTC (permalink / raw)
  To: Woradorn Laodhanadhaworn, rostedt
  Cc: mhiramat, mathieu.desnoyers, linux-kernel, linux-trace-kernel,
	linux-hardening, linux-kernel-mentees, shuah, skhan, me


> Op 20-06-2026 19:54 CEST schreef Woradorn Laodhanadhaworn <woradorn.laon@gmail.com>:
> 
>  
> In preparation for removing the strlcat API[1],
> replace the string concatenation logic with a struct seq_buf,
> which tracks the current position and the remaining space internally.
> 
> The backing buffer bootup_event_buf allocation is unchanged.
> Use seq_buf_str() to NUL-terminate before passing to early_enable_events().
> 
> Link: https://github.com/KSPP/linux/issues/370 [1]
> 
> Signed-off-by: Woradorn Laodhanadhaworn <woradorn.laon@gmail.com>
> ---
>  kernel/trace/trace_events.c | 21 ++++++++++++++++-----
>  1 file changed, 16 insertions(+), 5 deletions(-)
> 
> diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
> index c46e623e7e0d..15164723e028 100644
> --- a/kernel/trace/trace_events.c
> +++ b/kernel/trace/trace_events.c
> @@ -22,6 +22,7 @@
>  #include <linux/sort.h>
>  #include <linux/slab.h>
>  #include <linux/delay.h>
> +#include <linux/seq_buf.h>
>  
>  #include <trace/events/sched.h>
>  #include <trace/syscall.h>
> @@ -4501,13 +4502,23 @@ extern struct trace_event_call *__start_ftrace_events[];
>  extern struct trace_event_call *__stop_ftrace_events[];
>  
>  static char bootup_event_buf[COMMAND_LINE_SIZE] __initdata;

Isn't this now unused?

> +static struct seq_buf bootup_event_seq;
> +static bool bootup_event_seq_initialized;
>  

I think this can be refactored to avoid the bool. And should bootup_event_seq not be
__initdata?

>  static __init int setup_trace_event(char *str)
>  {
> -	if (bootup_event_buf[0] != '\0')
> -		strlcat(bootup_event_buf, ",", COMMAND_LINE_SIZE);
> +	if (!bootup_event_seq_initialized) {
> +		seq_buf_init(&bootup_event_seq, bootup_event_buf, COMMAND_LINE_SIZE);
> +		bootup_event_seq_initialized = true;
> +	}
> +
> +	if (seq_buf_used(&bootup_event_seq) > 0)
> +		seq_buf_puts(&bootup_event_seq, ",");
>  
> -	strlcat(bootup_event_buf, str, COMMAND_LINE_SIZE);
> +	seq_buf_puts(&bootup_event_seq, str);
> +
> +	if (seq_buf_has_overflowed(&bootup_event_seq))
> +		return -ENOMEM;
>  
>  	trace_set_ring_buffer_expanded(NULL);
>  	disable_tracing_selftest("running event tracing");
> @@ -4766,7 +4777,7 @@ static __init int event_trace_enable(void)
>  	 */
>  	__trace_early_add_events(tr);
>  
> -	early_enable_events(tr, bootup_event_buf, false);
> +	early_enable_events(tr, (char *)seq_buf_str(&bootup_event_seq), false);

What if trace_event is empty? Then setup_trace_event does not run AFAIK. See the
WARN_ON in seq_buf_str too. Have you tested this?

>  
>  	trace_printk_start_comm();
>  
> @@ -4794,7 +4805,7 @@ static __init int event_trace_enable_again(void)
>  	if (!tr)
>  		return -ENODEV;
>  
> -	early_enable_events(tr, bootup_event_buf, true);
> +	early_enable_events(tr, (char *)seq_buf_str(&bootup_event_seq), true);
>  
>  	return 0;
>  }
> -- 
> 2.43.0

Thanks,
Jori.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] tracing: Use seq_buf for string concatenation
  2026-06-20 17:54 [PATCH] tracing: Use seq_buf for string concatenation Woradorn Laodhanadhaworn
  2026-06-21 17:16 ` Jori Koolstra
@ 2026-06-22  5:20 ` XIAO WU
  1 sibling, 0 replies; 4+ messages in thread
From: XIAO WU @ 2026-06-22  5:20 UTC (permalink / raw)
  To: Woradorn Laodhanadhaworn, rostedt
  Cc: mhiramat, mathieu.desnoyers, linux-kernel, linux-trace-kernel,
	linux-hardening, linux-kernel-mentees, shuah, skhan, me,
	jkoolstra

Hi Woradorn,

Thanks for this cleanup — replacing strlcat with seq_buf is a nice
improvement in general, and it's good to see the KSPP goal of removing
strlcat making progress.

I came across an automated AI code review of this patch on the Sashiko
platform[1] that flagged a regression risk, and I was able to verify it
by testing the patched kernel in QEMU.

TL;DR: This patch introduces an unavoidable WARN_ON on every normal
boot (without the trace_event= parameter), which will crash systems
configured with panic_on_warn.

 > @@ -4501,13 +4502,23 @@ extern struct trace_event_call 
*__start_ftrace_events[];
 >  extern struct trace_event_call *__stop_ftrace_events[];
 >
 >  static char bootup_event_buf[COMMAND_LINE_SIZE] __initdata;
 > +static struct seq_buf bootup_event_seq;
 > +static bool bootup_event_seq_initialized;

The new seq_buf and the bool are not annotated __initdata, unlike
bootup_event_buf.  If bootup_event_buf is reclaimed after init, the
seq_buf will hold a dangling pointer to freed memory indefinitely.

 >  static __init int setup_trace_event(char *str)
 >  {
 > -    if (bootup_event_buf[0] != '\0')
 > -        strlcat(bootup_event_buf, ",", COMMAND_LINE_SIZE);
 > +    if (!bootup_event_seq_initialized) {
 > +        seq_buf_init(&bootup_event_seq, bootup_event_buf, 
COMMAND_LINE_SIZE);
 > +        bootup_event_seq_initialized = true;
 > +    }
 > +
 > +    if (seq_buf_used(&bootup_event_seq) > 0)
 > +        seq_buf_puts(&bootup_event_seq, ",");
 >
 > -    strlcat(bootup_event_buf, str, COMMAND_LINE_SIZE);
 > +    seq_buf_puts(&bootup_event_seq, str);

 > @@ -4766,7 +4777,7 @@ static __init int event_trace_enable(void)
 > -    early_enable_events(tr, bootup_event_buf, false);
 > +    early_enable_events(tr, (char *)seq_buf_str(&bootup_event_seq), 
false);

 > @@ -4794,7 +4805,7 @@ static __init int event_trace_enable_again(void)
 > -    early_enable_events(tr, bootup_event_buf, true);
 > +    early_enable_events(tr, (char *)seq_buf_str(&bootup_event_seq), 
true);

This is the main issue: both event_trace_enable() and
event_trace_enable_again() unconditionally call seq_buf_str() on the
still-zero-initialized seq_buf.  When booting without trace_event= on
the kernel command line, setup_trace_event() never runs, so
bootup_event_seq remains zero-initialized with size==0.  The
seq_buf_str() function hits WARN_ON(s->size == 0) in
include/linux/seq_buf.h:100.

I built a kernel with this patch applied and booted it in QEMU without
the trace_event= parameter.  The WARN_ON fires twice on every boot:

Kernel version: 7.1.0-next-20260618-gaa8f0a630773 #1 PREEMPT(full)

Boot 1, WARN 1 — trace_event_init():
```
[    4.339900][    T0] ------------[ cut here ]------------
[    4.340592][    T0] s->size == 0
[    4.340596][    T0] WARNING: include/linux/seq_buf.h:100
                          at trace_event_init+0x1370/0x16c0,
                          CPU#0: swapper/0/0
[    4.344985][    T0] RIP: 0010:trace_event_init+0x1370/0x16c0
[    4.355743][    T0] Call Trace:
[    4.356106][    T0]  <TASK>
[    4.356441][    T0]  trace_init+0x95/0x10c0
[    4.361793][    T0]  start_kernel+0x225/0x4e0
[    4.362409][    T0]  x86_64_start_reservations+0x18/0x30
[    4.362979][    T0]  x86_64_start_kernel+0x11a/0x160
[    4.363544][    T0]  common_startup_64+0x13e/0x158
[    4.363872][    T0]  </TASK>
```

Boot 1, WARN 2 — event_trace_enable_again():
```
[    4.760317][    T1] ------------[ cut here ]------------
[    4.760944][    T1] s->size == 0
[    4.760947][    T1] WARNING: include/linux/seq_buf.h:100
                          at event_trace_enable_again+0x1c2/0x230,
                          CPU#0: swapper/0/1
[    4.765947][    T1] RIP: 0010:event_trace_enable_again+0x1c2/0x230
[    4.777045][    T1] Call Trace:
[    4.777759][    T1]  do_one_initcall+0x128/0x700
[    4.779617][    T1]  kernel_init_freeable+0x398/0x940
[    4.780790][    T1]  kernel_init+0x21/0x2c0
[    4.781862][    T1]  ret_from_fork+0xb2c/0xdd0
[    4.782908][    T1]  </TASK>
```

These two WARN_ONs fire identically on every boot (verified across
three separate QEMU sessions).  On systems with panic_on_warn=1 this
will cause a kernel panic during every boot.

This matches what Jori already asked about in their review —
if the trace_event parameter is not provided, setup_trace_event() never
runs, and the uninitialized seq_buf triggers WARN_ON at both call sites.

Full PoC (compile with:  gcc -Wall -o poc poc.c):
```c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>

int main(void)
{
     char *log_buf;
     ssize_t log_size;
     const size_t buf_size = 4194304; /* 4 MB, enough for full dmesg */

     log_buf = calloc(1, buf_size);
     if (!log_buf) {
         perror("calloc");
         return 1;
     }

     /* SYSLOG_ACTION_READ_ALL = 3: read the full kernel ring buffer */
     log_size = syscall(SYS_syslog, 3, log_buf, buf_size - 1);
     if (log_size < 0) {
         perror("syslog(SYSLOG_ACTION_READ_ALL)");
         free(log_buf);
         return 1;
     }
     log_buf[log_size] = '\0';

     /*
      * The WARN_ON(s->size == 0) in include/linux/seq_buf.h:100
      * produces a message containing "s->size == 0" followed by
      * "WARNING:" within a few lines.  Search for this signature.
      */
     if (strstr(log_buf, "s->size == 0") &&
         strstr(log_buf, "seq_buf.h")) {
         printf("BUG TRIGGERED: WARN_ON in seq_buf_str() found in kernel 
log\n");

         /* Print the first occurrence with context */
         char *p = strstr(log_buf, "s->size == 0");
         char *start = p;
         while (start > log_buf && *start != '\n' && (p - start) < 512)
             start--;
         if (*start == '\n') start++;

         char *end = strstr(p, "---[ end trace");
         size_t len = end ? (size_t)(end - start + 30) : 2048;
         if (len > (size_t)(log_buf + log_size - start))
             len = log_buf + log_size - start;
         printf("%.*s\n", (int)len, start);
         free(log_buf);
         return 0;
     }

     printf("BUG NOT DETECTED: seq_buf WARN_ON not found.\n"
            "Either the kernel is unpatched or trace_event= was on the 
command line.\n");
     free(log_buf);
     return 1;
}
```

[1] 
https://sashiko.dev/#/patchset/20260620175441.223342-1-woradorn.laon%40gmail.com

Thanks,
Xiao



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] tracing: Use seq_buf for string concatenation
  2026-06-21 17:16 ` Jori Koolstra
@ 2026-06-22  8:18   ` Woradorn Laodhanadhaworn
  0 siblings, 0 replies; 4+ messages in thread
From: Woradorn Laodhanadhaworn @ 2026-06-22  8:18 UTC (permalink / raw)
  To: Jori Koolstra, rostedt
  Cc: mhiramat, mathieu.desnoyers, linux-kernel, linux-trace-kernel,
	linux-hardening, linux-kernel-mentees, shuah, skhan, me

On 22/6/2569 BE 00:16, Jori Koolstra wrote:
> 
>> Op 20-06-2026 19:54 CEST schreef Woradorn Laodhanadhaworn <woradorn.laon@gmail.com>:
>>
>>  
>> In preparation for removing the strlcat API[1],
>> replace the string concatenation logic with a struct seq_buf,
>> which tracks the current position and the remaining space internally.
>>
>> The backing buffer bootup_event_buf allocation is unchanged.
>> Use seq_buf_str() to NUL-terminate before passing to early_enable_events().
>>
>> Link: https://github.com/KSPP/linux/issues/370 [1]
>>
>> Signed-off-by: Woradorn Laodhanadhaworn <woradorn.laon@gmail.com>
>> ---
>>  kernel/trace/trace_events.c | 21 ++++++++++++++++-----
>>  1 file changed, 16 insertions(+), 5 deletions(-)
>>
>> diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
>> index c46e623e7e0d..15164723e028 100644
>> --- a/kernel/trace/trace_events.c
>> +++ b/kernel/trace/trace_events.c
>> @@ -22,6 +22,7 @@
>>  #include <linux/sort.h>
>>  #include <linux/slab.h>
>>  #include <linux/delay.h>
>> +#include <linux/seq_buf.h>
>>  
>>  #include <trace/events/sched.h>
>>  #include <trace/syscall.h>
>> @@ -4501,13 +4502,23 @@ extern struct trace_event_call *__start_ftrace_events[];
>>  extern struct trace_event_call *__stop_ftrace_events[];
>>  
>>  static char bootup_event_buf[COMMAND_LINE_SIZE] __initdata;
> 
> Isn't this now unused?
> 
>> +static struct seq_buf bootup_event_seq;
>> +static bool bootup_event_seq_initialized;
>>  
> 
> I think this can be refactored to avoid the bool. And should bootup_event_seq not be
> __initdata?
> 
>>  static __init int setup_trace_event(char *str)
>>  {
>> -	if (bootup_event_buf[0] != '\0')
>> -		strlcat(bootup_event_buf, ",", COMMAND_LINE_SIZE);
>> +	if (!bootup_event_seq_initialized) {
>> +		seq_buf_init(&bootup_event_seq, bootup_event_buf, COMMAND_LINE_SIZE);
>> +		bootup_event_seq_initialized = true;
>> +	}
>> +
>> +	if (seq_buf_used(&bootup_event_seq) > 0)
>> +		seq_buf_puts(&bootup_event_seq, ",");
>>  
>> -	strlcat(bootup_event_buf, str, COMMAND_LINE_SIZE);
>> +	seq_buf_puts(&bootup_event_seq, str);
>> +
>> +	if (seq_buf_has_overflowed(&bootup_event_seq))
>> +		return -ENOMEM;
>>  
>>  	trace_set_ring_buffer_expanded(NULL);
>>  	disable_tracing_selftest("running event tracing");
>> @@ -4766,7 +4777,7 @@ static __init int event_trace_enable(void)
>>  	 */
>>  	__trace_early_add_events(tr);
>>  
>> -	early_enable_events(tr, bootup_event_buf, false);
>> +	early_enable_events(tr, (char *)seq_buf_str(&bootup_event_seq), false);
> 
> What if trace_event is empty? Then setup_trace_event does not run AFAIK. See the
> WARN_ON in seq_buf_str too. Have you tested this?
> 
>>  
>>  	trace_printk_start_comm();
>>  
>> @@ -4794,7 +4805,7 @@ static __init int event_trace_enable_again(void)
>>  	if (!tr)
>>  		return -ENODEV;
>>  
>> -	early_enable_events(tr, bootup_event_buf, true);
>> +	early_enable_events(tr, (char *)seq_buf_str(&bootup_event_seq), true);
>>  
>>  	return 0;
>>  }
>> -- 
>> 2.43.0
> 
> Thanks,
> Jori.

Thank you, Jori, for your review. I will send v2.
I tested both empty and non-empty trace_event cases with QEMU, and both now boot successfully. 

The logs below demonstrate this.

Non-empty trace_event:

qemu-system-x86_64 \
    -kernel arch/x86/boot/bzImage \
    -initrd initramfs.cpio.gz \
    -append "console=ttyS0 trace_event=:mod:rproc_qcom_common,:mod:qrtr,:mod:qcom_aoss trace_event=:mod:rproc_qcom_common" \
    -nographic \
    -m 512M

[    0.082316] Kernel command line: console=ttyS0 trace_event=:mod:rproc_qcom_common,:mod:qrtr,:mod:qcom_aoss trace_event=:mod:rproc_qcom_common
[    0.083324] bootup_event_buf: :mod:rproc_qcom_common,:mod:qrtr,:mod:qcom_aoss
[    0.083413] bootup_event_buf: :mod:rproc_qcom_common,:mod:qrtr,:mod:qcom_aoss,:mod:rproc_qcom_common
[    0.083689] printk: log buffer data + meta data: 262144 + 917504 = 1179648 bytes

Empty trace_event:

qemu-system-x86_64 \
    -kernel arch/x86/boot/bzImage \
    -initrd initramfs.cpio.gz \
    -append "console=ttyS0" \
    -nographic \
    -m 512M

[    0.085213] Kernel command line: console=ttyS0
[    0.086458] printk: log buffer data + meta data: 262144 + 917504 = 1179648 bytes

Thanks,
Woradorn

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-06-22  8:18 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-20 17:54 [PATCH] tracing: Use seq_buf for string concatenation Woradorn Laodhanadhaworn
2026-06-21 17:16 ` Jori Koolstra
2026-06-22  8:18   ` Woradorn Laodhanadhaworn
2026-06-22  5:20 ` XIAO WU

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox