mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v4 00/11] seq_buf: Add seq_buf_strlen()
@ 2026-10-03  3:59 Kees Cook
  2026-10-03  3:59 ` [PATCH v4 01/11] seq_buf: Do not print an empty line from an overflowed seq_buf_do_printk() Kees Cook
                   ` (11 more replies)
  0 siblings, 12 replies; 19+ messages in thread
From: Kees Cook @ 2026-10-03  3:59 UTC (permalink / raw)
  To: Bill Wendling
  Cc: Kees Cook, Jonathan Corbet, Andrew Morton, David Gow,
	Petr Mladek, Sergey Senozhatsky, Shuvam Pandey, Steven Rostedt,
	Günther Noack, Matthew Wilcox (Oracle),
	Mickaël Salaün, Andy Shevchenko, Masami Hiramatsu,
	Mathieu Desnoyers, Jiri Kosina, Alexei Starovoitov,
	Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jiri Olsa, Emil Tsalapatis, Ihor Solodrai,
	Christophe Leroy (CS GROUP),
	Uwe Kleine-König, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Shivaprasad G Bhat, Thorsten Blum,
	Alison Schofield, Dave Jiang, Greg Kroah-Hartman, Guangshuo Li,
	Ira Weiny, Uwe Kleine-König, Vishal Verma, Randy Dunlap,
	Shuah Khan, linux-kernel, bpf, linux-security-module,
	linux-trace-kernel, linuxppc-dev, nvdimm, linux-doc,
	linux-hardening

Hi,

While working on seq_buf conversions[1], we found there was a need for
seq_buf_strlen() (since calling strlen(seq_buf_str()) would be a waste
of time: seq_buf already knows the length), seq_buf_init_append() (to
start a seq_buf from an existing string buffer), and seq_buf_terminate()
(as there are many callers using seq_buf_str() just for its termination
side-effect). And while implementing that, we found a bunch of other
related things that needed fixing. This is that ever-growing series,
with tests for each fix.

 v4:
  - add trailers from v3
  - 03/11, 04/11: export seq_buf_putmem() and seq_buf_path(), which the
    tests call, so they also build as a module
  - 08/11: start out overflowed when the buffer has no NUL within its
    size, as strlcat() treats it as already truncated (Andy)
  - 09/11: use seq_buf_strlen() for flags_show()'s check whether any
    flag was printed, too (Madhavan)
  - 01/11, 05/11: drop doubled blank lines and re-wrap a declaration in
    the tests (Andy)
  - v3..v4 diff: https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git/diff/?id=dev/v7.3-rc2/seq_buf/v4&id2=dev/v7.3-rc2/seq_buf/v3
 v3: https://lore.kernel.org/all/20260930235231.out.387-kees@kernel.org/
 v2: https://lore.kernel.org/all/20260919002658.stay.929-kees@kernel.org/
 v1: https://lore.kernel.org/all/20260917002312.i.923-kees@kernel.org/

Thanks!

-Kees

[1] https://lore.kernel.org/all/20260917011359.1585961-1-morbo@google.com/

Bill Wendling (1):
  seq_buf: Add seq_buf_init_append()

Kees Cook (10):
  seq_buf: Do not print an empty line from an overflowed
    seq_buf_do_printk()
  seq_buf: Do not pop from an overflowed seq_buf
  seq_buf: Copy what fits when seq_buf_puts() and seq_buf_putmem()
    overflow
  seq_buf: Clear what a writer did not claim when a seq_buf overflows
  seq_buf: Add seq_buf_strlen()
  seq_buf: Add seq_buf_terminate()
  bpf: Remove dead newline stripping from format_disasm_line()
  powerpc/papr_scm: Return the string length from the sysfs show
    functions
  nvdimm: ndtest: Return the string length from flags_show()
  docs: core-api: Document the seq_buf API

 Documentation/core-api/kernel-api.rst     |   9 +
 include/linux/seq_buf.h                   | 139 +++++-
 include/linux/trace_seq.h                 |   5 +-
 arch/powerpc/platforms/pseries/papr_scm.c |   6 +-
 kernel/bpf/diagnostics.c                  |   8 +-
 kernel/trace/trace_events.c               |   4 +-
 kernel/trace/trace_events_hist.c          |   6 +-
 lib/seq_buf.c                             |  37 +-
 lib/tests/seq_buf_kunit.c                 | 528 +++++++++++++++++++++-
 tools/testing/nvdimm/test/ndtest.c        |   2 +-
 10 files changed, 708 insertions(+), 36 deletions(-)

-- 
2.55.0


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

* [PATCH v4 01/11] seq_buf: Do not print an empty line from an overflowed seq_buf_do_printk()
  2026-10-03  3:59 [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Kees Cook
@ 2026-10-03  3:59 ` Kees Cook
  2026-10-03  4:50   ` bot+bpf-ci
  2026-10-03  3:59 ` [PATCH v4 02/11] seq_buf: Do not pop from an overflowed seq_buf Kees Cook
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 19+ messages in thread
From: Kees Cook @ 2026-10-03  3:59 UTC (permalink / raw)
  To: Bill Wendling
  Cc: Kees Cook, Andrew Morton, David Gow, Petr Mladek,
	Sergey Senozhatsky, Shuvam Pandey, Steven Rostedt,
	Jonathan Corbet, Günther Noack, Matthew Wilcox (Oracle),
	Mickaël Salaün, Andy Shevchenko, Masami Hiramatsu,
	Mathieu Desnoyers, Jiri Kosina, Alexei Starovoitov,
	Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jiri Olsa, Emil Tsalapatis, Ihor Solodrai,
	Christophe Leroy (CS GROUP),
	Uwe Kleine-König, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Shivaprasad G Bhat, Thorsten Blum,
	Alison Schofield, Dave Jiang, Greg Kroah-Hartman, Guangshuo Li,
	Ira Weiny, Uwe Kleine-König, Vishal Verma, Randy Dunlap,
	Shuah Khan, linux-kernel, bpf, linux-security-module,
	linux-trace-kernel, linuxppc-dev, nvdimm, linux-doc,
	linux-hardening

seq_buf_do_printk() prints a buffer line by line, then prints whatever
follows the last newline. When a string has overflowed at exactly a
newline, an empty line is printed since the pointer hasn't reached the
overflow mark of the seq_buf. Switch to just check if the string is
already empty and only print if not.

The only caller is the memory cgroup OOM report, so this could only ever
add a blank line to a report whose statistics already did not fit.

Add a test that registers a console to count the records that
seq_buf_do_printk() emits. It counts the records carrying the test's
marker, and the records holding nothing but a line feed that arrive
after one, so that unrelated kernel messages do not disturb it. Both
states that reach the flaw are covered: exactly full, and overflowed.

Tests passed under qemu on ARCH=x86_64 with GCC 16.2.0 and CONFIG_KASAN=y,
and on big-endian ARCH=s390 with GCC s390x-linux-gnu 16.2.0.

Fixes: 96928d9032a7c ("seq_buf: Add seq_buf_do_printk() helper")
Assisted-by: LLM
Signed-off-by: Kees Cook <kees@kernel.org>
---
 lib/seq_buf.c             |   2 +-
 lib/tests/seq_buf_kunit.c | 132 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 133 insertions(+), 1 deletion(-)

diff --git a/lib/seq_buf.c b/lib/seq_buf.c
index a92093f346da..35a5964370b4 100644
--- a/lib/seq_buf.c
+++ b/lib/seq_buf.c
@@ -128,7 +128,7 @@ void seq_buf_do_printk(struct seq_buf *s, const char *lvl)
 	}
 
 	/* No trailing LF */
-	if (start < s->buffer + s->len)
+	if (*start)
 		printk("%s%s\n", lvl, start);
 }
 EXPORT_SYMBOL_GPL(seq_buf_do_printk);
diff --git a/lib/tests/seq_buf_kunit.c b/lib/tests/seq_buf_kunit.c
index eb466386bbef..0934dfb602ff 100644
--- a/lib/tests/seq_buf_kunit.c
+++ b/lib/tests/seq_buf_kunit.c
@@ -6,7 +6,9 @@
  */
 
 #include <kunit/test.h>
+#include <linux/console.h>
 #include <linux/seq_buf.h>
+#include <linux/string.h>
 
 static void seq_buf_init_test(struct kunit *test)
 {
@@ -216,6 +218,135 @@ static void seq_buf_putmem_hex_overflow_test(struct kunit *test)
 	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected);
 }
 
+/*
+ * Counters for the console that seq_buf_do_printk_test() registers while it
+ * runs. Only records carrying the marker are counted, so unrelated kernel
+ * messages do not disturb them.
+ *
+ * An empty record carries nothing to recognize it by, so count one only
+ * where the flaw puts it: directly after a record of ours, with nothing in
+ * between. That still misreads a bare line feed printed by another CPU in
+ * exactly that gap, but no longer counts one printed at any point while the
+ * console happens to be registered.
+ */
+#define SEQ_BUF_PRINTK_MARKER	"sbdpkx"
+
+static unsigned int seq_buf_printk_marked;
+static unsigned int seq_buf_printk_empty;
+static bool seq_buf_printk_last_was_ours;
+
+static void seq_buf_printk_capture(struct console *con,
+				   const char *s, unsigned int count)
+{
+	const char *text = s;
+	const char *prefix;
+
+	/*
+	 * Skip what printk() puts in front of the message: a timestamp,
+	 * and the caller id as well under CONFIG_PRINTK_CALLER, so strip
+	 * every bracketed group rather than just the first.
+	 */
+	while (count && text[0] == '[') {
+		prefix = memchr(text, ']', count);
+		if (!prefix)
+			break;
+		count -= prefix + 1 - text;
+		text = prefix + 1;
+		if (count && text[0] == ' ') {
+			text++;
+			count--;
+		}
+	}
+
+	if (strnstr(text, SEQ_BUF_PRINTK_MARKER, count)) {
+		seq_buf_printk_marked++;
+		seq_buf_printk_last_was_ours = true;
+		return;
+	}
+
+	if (seq_buf_printk_last_was_ours &&
+	    (count == 0 || (count == 1 && text[0] == '\n')))
+		seq_buf_printk_empty++;
+
+	seq_buf_printk_last_was_ours = false;
+}
+
+static void seq_buf_printk_run(struct console *capture, struct seq_buf *s)
+{
+	seq_buf_printk_marked = 0;
+	seq_buf_printk_empty = 0;
+	seq_buf_printk_last_was_ours = false;
+
+	/*
+	 * register_console() will not take an unmatched console without
+	 * CON_ENABLED, and unregister_console() clears it, so set it on
+	 * every run to keep the test repeatable.
+	 */
+	capture->flags = CON_ENABLED;
+	register_console(capture);
+	seq_buf_do_printk(s, KERN_INFO);
+	unregister_console(capture);
+}
+
+static void seq_buf_do_printk_test(struct kunit *test)
+{
+	/*
+	 * A registered console is a global object: printk() reaches it
+	 * through the console list from any CPU, and the console code writes
+	 * back into it, so keep it out of this function's stack frame the
+	 * way every other console in the tree does.
+	 */
+	static struct console capture = {
+		.name = "sbufcap",
+		.write = seq_buf_printk_capture,
+		.index = -1,
+	};
+	DECLARE_SEQ_BUF(s, 8);
+	DECLARE_SEQ_BUF(t, 16);
+	DECLARE_SEQ_BUF(u, 8);
+
+	/*
+	 * Fill the buffer exactly, so that the NUL takes the place of the
+	 * last byte and the string ends with the line feed before it.
+	 */
+	seq_buf_puts(&s, SEQ_BUF_PRINTK_MARKER);
+	seq_buf_putc(&s, '\n');
+	seq_buf_putc(&s, '!');
+	KUNIT_ASSERT_FALSE(test, seq_buf_has_overflowed(&s));
+	KUNIT_ASSERT_EQ(test, seq_buf_used(&s), 8);
+	KUNIT_ASSERT_EQ(test, strlen(seq_buf_str(&s)), 7);
+
+	seq_buf_printk_run(&capture, &s);
+
+	/* The one line that was written, and nothing after it. */
+	KUNIT_EXPECT_EQ(test, seq_buf_printk_marked, 1);
+	KUNIT_EXPECT_EQ(test, seq_buf_printk_empty, 0);
+
+	/* Check that lines without a trailing newline are shown. */
+	seq_buf_puts(&t, SEQ_BUF_PRINTK_MARKER "\n" SEQ_BUF_PRINTK_MARKER);
+	KUNIT_ASSERT_FALSE(test, seq_buf_has_overflowed(&t));
+
+	seq_buf_printk_run(&capture, &t);
+
+	KUNIT_EXPECT_EQ(test, seq_buf_printk_marked, 2);
+	KUNIT_EXPECT_EQ(test, seq_buf_printk_empty, 0);
+
+	/*
+	 * The buffer above was exactly full, where "len" equals the size. A
+	 * buffer that actually overflowed reaches the same bug by the other
+	 * route the old test had, with "len" one past the size.
+	 */
+	seq_buf_puts(&u, SEQ_BUF_PRINTK_MARKER "\n");
+	KUNIT_EXPECT_EQ(test, seq_buf_puts(&u, "yy"), -1);
+	KUNIT_ASSERT_TRUE(test, seq_buf_has_overflowed(&u));
+	KUNIT_ASSERT_EQ(test, u.len, u.size + 1);
+
+	seq_buf_printk_run(&capture, &u);
+
+	KUNIT_EXPECT_EQ(test, seq_buf_printk_marked, 1);
+	KUNIT_EXPECT_EQ(test, seq_buf_printk_empty, 0);
+}
+
 static struct kunit_case seq_buf_test_cases[] = {
 	KUNIT_CASE(seq_buf_init_test),
 	KUNIT_CASE(seq_buf_declare_test),
@@ -228,6 +359,7 @@ static struct kunit_case seq_buf_test_cases[] = {
 	KUNIT_CASE(seq_buf_get_buf_commit_test),
 	KUNIT_CASE(seq_buf_putmem_hex_test),
 	KUNIT_CASE(seq_buf_putmem_hex_overflow_test),
+	KUNIT_CASE(seq_buf_do_printk_test),
 	{}
 };
 
-- 
2.55.0


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

* [PATCH v4 02/11] seq_buf: Do not pop from an overflowed seq_buf
  2026-10-03  3:59 [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Kees Cook
  2026-10-03  3:59 ` [PATCH v4 01/11] seq_buf: Do not print an empty line from an overflowed seq_buf_do_printk() Kees Cook
@ 2026-10-03  3:59 ` Kees Cook
  2026-10-03  3:59 ` [PATCH v4 03/11] seq_buf: Copy what fits when seq_buf_puts() and seq_buf_putmem() overflow Kees Cook
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Kees Cook @ 2026-10-03  3:59 UTC (permalink / raw)
  To: Bill Wendling
  Cc: Kees Cook, Günther Noack, Matthew Wilcox (Oracle),
	Mickaël Salaün, bpf, linux-security-module,
	linux-trace-kernel, Andrew Morton, Andy Shevchenko, David Gow,
	Masami Hiramatsu, Mathieu Desnoyers, Petr Mladek, Shuvam Pandey,
	Steven Rostedt, Jonathan Corbet, Sergey Senozhatsky, Jiri Kosina,
	Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
	Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
	Ihor Solodrai, Christophe Leroy (CS GROUP),
	Uwe Kleine-König, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Shivaprasad G Bhat, Thorsten Blum,
	Alison Schofield, Dave Jiang, Greg Kroah-Hartman, Guangshuo Li,
	Ira Weiny, Uwe Kleine-König, Vishal Verma, Randy Dunlap,
	Shuah Khan, linux-kernel, linuxppc-dev, nvdimm, linux-doc,
	linux-hardening

When a seq_buf has overflowed, its len is size + 1, so seq_buf_pop()
decrements len to size and reads buffer[size], one byte past the end of
the buffer. It also leaves len equal to size, which no longer counts as
overflowed, so a truncated seq_buf then looks like a complete, full one.

An overflowed seq_buf logically has no last character to pop: the
length of what was written has been lost, and the last byte of the
buffer may be the NUL written by vsnprintf() or bytes that were never
committed. Return -1 for an overflowed seq_buf, as for an empty one,
and leave it overflowed, as the rest of the seq_buf API does until
seq_buf_clear() or seq_buf_init().

The current callers do not reach this, e.g. trace_syscalls only calls
trace_seq_pop() when the trace_seq it pops from has not overflowed, and
kernel/bpf/diagnostics.c sets the length from strnlen() before popping.

Add tests for the pop corner cases.

Tests passed under qemu on ARCH=x86_64 with GCC 16.2.0 and CONFIG_KASAN=y,
and on big-endian ARCH=s390 with GCC s390x-linux-gnu 16.1.0.

Fixes: 32e0f607ac6a2 ("tracing: Add trace_seq_pop() and seq_buf_pop()")
Assisted-by: LLM
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Kees Cook <kees@kernel.org>
---
 include/linux/seq_buf.h   |  4 ++--
 include/linux/trace_seq.h |  5 ++++-
 lib/tests/seq_buf_kunit.c | 42 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 48 insertions(+), 3 deletions(-)

diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
index 9f2839e73f8a..f5a350347bc5 100644
--- a/include/linux/seq_buf.h
+++ b/include/linux/seq_buf.h
@@ -155,11 +155,11 @@ static inline void seq_buf_commit(struct seq_buf *s, int num)
  *
  * Removes the last written character to the seq_buf @s.
  *
- * Returns the last character or -1 if it is empty.
+ * Returns the last character, or -1 if @s is empty or has overflowed.
  */
 static inline int seq_buf_pop(struct seq_buf *s)
 {
-	if (!s->len)
+	if (!s->len || seq_buf_has_overflowed(s))
 		return -1;
 
 	s->len--;
diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h
index 697d619aafdc..7174ebf3f015 100644
--- a/include/linux/trace_seq.h
+++ b/include/linux/trace_seq.h
@@ -86,7 +86,10 @@ static inline bool trace_seq_has_overflowed(struct trace_seq *s)
  *
  * Removes the last written character to the trace_seq @s.
  *
- * Returns the last character or -1 if it is empty.
+ * Returns the last character, or -1 if the underlying seq_buf is empty or
+ * has overflowed. Note that only that buffer is consulted: a @s marked
+ * full by a write that did not fit, which trace_seq_has_overflowed()
+ * reports as overflowed, still pops the last character written.
  */
 static inline int trace_seq_pop(struct trace_seq *s)
 {
diff --git a/lib/tests/seq_buf_kunit.c b/lib/tests/seq_buf_kunit.c
index 0934dfb602ff..de491f96c1ac 100644
--- a/lib/tests/seq_buf_kunit.c
+++ b/lib/tests/seq_buf_kunit.c
@@ -115,6 +115,47 @@ static void seq_buf_putc_test(struct kunit *test)
 	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "");
 }
 
+static void seq_buf_pop_test(struct kunit *test)
+{
+	DECLARE_SEQ_BUF(s, 8);
+	struct seq_buf t;
+	char *buf;
+
+	/* Nothing to pop. */
+	KUNIT_EXPECT_EQ(test, seq_buf_pop(&s), -1);
+	KUNIT_EXPECT_EQ(test, s.len, 0);
+
+	seq_buf_puts(&s, "hello");
+	KUNIT_EXPECT_EQ(test, seq_buf_pop(&s), 'o');
+	KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 4);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hell");
+
+	/* A 0xff byte must not be mistaken for an empty buffer. */
+	seq_buf_putc(&s, 0xff);
+	KUNIT_EXPECT_EQ(test, seq_buf_pop(&s), 0xff);
+
+	/* A full buffer pops its last byte. */
+	seq_buf_puts(&s, "abc");
+	seq_buf_putc(&s, 'd');
+	KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&s));
+	KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 8);
+	KUNIT_EXPECT_EQ(test, seq_buf_pop(&s), 'd');
+	KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 7);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hellabc");
+
+	/*
+	 * An overflowed buffer has nothing to pop, and stays overflowed. Use
+	 * a buffer allocated at its exact size, so that KASAN reports any
+	 * read past its end.
+	 */
+	buf = kunit_kmalloc(test, 16, GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, buf);
+	seq_buf_init(&t, buf, 16);
+	KUNIT_EXPECT_EQ(test, seq_buf_printf(&t, "%s", "longer than sixteen"), -1);
+	KUNIT_EXPECT_EQ(test, seq_buf_pop(&t), -1);
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&t));
+}
+
 static void seq_buf_printf_test(struct kunit *test)
 {
 	DECLARE_SEQ_BUF(s, 32);
@@ -354,6 +395,7 @@ static struct kunit_case seq_buf_test_cases[] = {
 	KUNIT_CASE(seq_buf_puts_test),
 	KUNIT_CASE(seq_buf_puts_overflow_test),
 	KUNIT_CASE(seq_buf_putc_test),
+	KUNIT_CASE(seq_buf_pop_test),
 	KUNIT_CASE(seq_buf_printf_test),
 	KUNIT_CASE(seq_buf_printf_overflow_test),
 	KUNIT_CASE(seq_buf_get_buf_commit_test),
-- 
2.55.0


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

* [PATCH v4 03/11] seq_buf: Copy what fits when seq_buf_puts() and seq_buf_putmem() overflow
  2026-10-03  3:59 [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Kees Cook
  2026-10-03  3:59 ` [PATCH v4 01/11] seq_buf: Do not print an empty line from an overflowed seq_buf_do_printk() Kees Cook
  2026-10-03  3:59 ` [PATCH v4 02/11] seq_buf: Do not pop from an overflowed seq_buf Kees Cook
@ 2026-10-03  3:59 ` Kees Cook
  2026-10-03  4:50   ` bot+bpf-ci
  2026-10-03  3:59 ` [PATCH v4 04/11] seq_buf: Clear what a writer did not claim when a seq_buf overflows Kees Cook
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 19+ messages in thread
From: Kees Cook @ 2026-10-03  3:59 UTC (permalink / raw)
  To: Bill Wendling
  Cc: Kees Cook, Matthew Wilcox (Oracle),
	Andrew Morton, Andy Shevchenko, David Gow, Petr Mladek,
	Shuvam Pandey, Steven Rostedt, Jonathan Corbet,
	Sergey Senozhatsky, Günther Noack, Mickaël Salaün,
	Masami Hiramatsu, Mathieu Desnoyers, Jiri Kosina,
	Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
	Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
	Ihor Solodrai, Christophe Leroy (CS GROUP),
	Uwe Kleine-König, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Shivaprasad G Bhat, Thorsten Blum,
	Alison Schofield, Dave Jiang, Greg Kroah-Hartman, Guangshuo Li,
	Ira Weiny, Uwe Kleine-König, Vishal Verma, Randy Dunlap,
	Shuah Khan, linux-kernel, bpf, linux-security-module,
	linux-trace-kernel, linuxppc-dev, nvdimm, linux-doc,
	linux-hardening

When seq_buf_puts() or seq_buf_putmem() is given more than fits, it
copies nothing and only marks the seq_buf as overflowed. If seq_buf_str()
is used, it will terminate the buffer in its last byte, so every byte
between the end of the data and the end of the buffer becomes part of
the string, though the seq_buf never wrote them.

seq_buf_printf() does not have this problem, because vsnprintf() writes
as much of the output as fits, followed by a NUL. Repeat this behavior
in seq_buf_puts(), using strscpy(), and in seq_buf_putmem(), which also
covers seq_buf_putmem_hex(). seq_buf_putc() needs no change, as it can
only overflow when the buffer is already full.

Each writer now records the buffer as full once it has copied what fits,
so that what it wrote can be told apart from bytes nothing touched.

Update seq_buf_putmem_hex_overflow_test, which expected a hex group that
did not fit whole to be left out entirely, and add tests that overflow
seq_buf_puts(), seq_buf_putmem() and seq_buf_putmem_hex() with stale
bytes in the buffer.

The three partial-overflow tests check the buffer itself rather than
seq_buf_str(). Neither writer leaves the last byte of the buffer alone:
seq_buf_putmem() copies raw bytes and writes no NUL, and seq_buf_puts()
relies on strscpy() to write one. That byte is exactly where
seq_buf_str() writes its terminator, so asserting only on the string
would pass whether the copy stopped a byte early or dropped the NUL
entirely.

seq_buf_putmem() was never exported, unlike the other writers, so the
test failed to link as a module. Export it.

Tests passed under qemu on ARCH=x86_64 with GCC 16.2.0 and CONFIG_KASAN=y,
and on big-endian ARCH=s390 with GCC s390x-linux-gnu 16.2.0, and the
test builds as a module (CONFIG_SEQ_BUF_KUNIT_TEST=m).

Assisted-by: LLM
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Kees Cook <kees@kernel.org>
---
 include/linux/seq_buf.h   |  6 +++
 lib/seq_buf.c             | 17 +++++++-
 lib/tests/seq_buf_kunit.c | 85 ++++++++++++++++++++++++++++++++++++++-
 3 files changed, 104 insertions(+), 4 deletions(-)

diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
index f5a350347bc5..77e76e283370 100644
--- a/include/linux/seq_buf.h
+++ b/include/linux/seq_buf.h
@@ -55,6 +55,12 @@ seq_buf_has_overflowed(struct seq_buf *s)
 	return s->len > s->size;
 }
 
+/*
+ * Mark @s as overflowed, which discards the length of what it holds. The
+ * bytes up to its last one are the string from then on, as that is where
+ * seq_buf_str() terminates it, so a caller that could not fill the buffer
+ * has to NUL them itself before calling this.
+ */
 static inline void
 seq_buf_set_overflow(struct seq_buf *s)
 {
diff --git a/lib/seq_buf.c b/lib/seq_buf.c
index 35a5964370b4..60e9eadb3ef7 100644
--- a/lib/seq_buf.c
+++ b/lib/seq_buf.c
@@ -175,7 +175,9 @@ int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary)
  * @s: seq_buf descriptor
  * @str: simple string to record
  *
- * Copy a simple string into the sequence buffer.
+ * Copy a simple string into the sequence buffer. If @str does not fit,
+ * as much of it as fits is copied, followed by a null byte, as
+ * seq_buf_printf() does.
  *
  * Returns: zero on success, -1 on overflow.
  */
@@ -194,6 +196,11 @@ int seq_buf_puts(struct seq_buf *s, const char *str)
 		s->len += len - 1;
 		return 0;
 	}
+	/* Copy what fits, so the buffer never holds stale bytes */
+	if (s->len < s->size) {
+		strscpy(s->buffer + s->len, str, s->size - s->len);
+		s->len = s->size;
+	}
 	seq_buf_set_overflow(s);
 	return -1;
 }
@@ -229,7 +236,7 @@ EXPORT_SYMBOL_GPL(seq_buf_putc);
  *
  * There may be cases where raw memory needs to be written into the
  * buffer and a strcpy() would not work. Using this function allows
- * for such cases.
+ * for such cases. If @mem does not fit, as much of it as fits is copied.
  *
  * Returns: zero on success, -1 on overflow.
  */
@@ -242,9 +249,15 @@ int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len)
 		s->len += len;
 		return 0;
 	}
+	/* Copy what fits, so the buffer never holds stale bytes */
+	if (s->len < s->size) {
+		memcpy(s->buffer + s->len, mem, s->size - s->len);
+		s->len = s->size;
+	}
 	seq_buf_set_overflow(s);
 	return -1;
 }
+EXPORT_SYMBOL_GPL(seq_buf_putmem);
 
 #define MAX_MEMHEX_BYTES	8U
 #define HEX_CHARS		(MAX_MEMHEX_BYTES*2 + 1)
diff --git a/lib/tests/seq_buf_kunit.c b/lib/tests/seq_buf_kunit.c
index de491f96c1ac..046614100c77 100644
--- a/lib/tests/seq_buf_kunit.c
+++ b/lib/tests/seq_buf_kunit.c
@@ -248,9 +248,9 @@ static void seq_buf_putmem_hex_overflow_test(struct kunit *test)
 	DECLARE_SEQ_BUF(s, 20);
 	const u8 data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 #ifdef __BIG_ENDIAN
-	const char *expected = "0001020304050607 ";
+	const char *expected = "0001020304050607 08";
 #else
-	const char *expected = "0706050403020100 ";
+	const char *expected = "0706050403020100 09";
 #endif
 
 	KUNIT_EXPECT_EQ(test, seq_buf_putmem_hex(&s, data, sizeof(data)), -1);
@@ -259,6 +259,84 @@ static void seq_buf_putmem_hex_overflow_test(struct kunit *test)
 	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected);
 }
 
+static void seq_buf_puts_partial_overflow_test(struct kunit *test)
+{
+	static const char expected[] = "abcdefg";
+	DECLARE_SEQ_BUF(s, 16);
+	struct seq_buf t;
+	char buf[8];
+
+	/* As much of the string as fits is written, like seq_buf_printf(). */
+	seq_buf_puts(&s, "hello");
+	KUNIT_EXPECT_EQ(test, seq_buf_puts(&s, " world, again"), -1);
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
+	KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 16);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hello world, ag");
+
+	/* Stale bytes after the data must not show up in the string. */
+	memset(buf, 'X', sizeof(buf));
+	seq_buf_init(&t, buf, sizeof(buf));
+	seq_buf_putc(&t, 'a');
+	KUNIT_EXPECT_EQ(test, seq_buf_puts(&t, "bcdefghij"), -1);
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&t));
+
+	/*
+	 * Check the buffer before seq_buf_str() does: it would write the
+	 * terminator over the last byte itself, hiding whether the copy
+	 * placed one there. The literal's own NUL is the eighth byte.
+	 */
+	KUNIT_EXPECT_MEMEQ(test, buf, expected, sizeof(buf));
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&t), "abcdefg");
+}
+
+static void seq_buf_putmem_partial_overflow_test(struct kunit *test)
+{
+	const u8 data[] = { 1, 2, 3, 4, 5, 6, 7 };
+	const char expected[] = { 'a', 'b', 1, 2, 3, 4, 5, 6 };
+	struct seq_buf s;
+	char buf[8];
+
+	memset(buf, 'X', sizeof(buf));
+	seq_buf_init(&s, buf, sizeof(buf));
+	seq_buf_putmem(&s, "ab", 2);
+
+	/* One byte too many, so the last byte of @data is dropped. */
+	KUNIT_EXPECT_EQ(test, seq_buf_putmem(&s, data, sizeof(data)), -1);
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
+
+	/*
+	 * Check the buffer rather than seq_buf_str(): seq_buf_putmem() writes
+	 * no NUL of its own, so a short copy leaves a stale byte at the end,
+	 * exactly where seq_buf_str() would then write the terminator and
+	 * hide it.
+	 */
+	KUNIT_EXPECT_MEMEQ(test, buf, expected, sizeof(buf));
+}
+
+static void seq_buf_putmem_hex_partial_overflow_test(struct kunit *test)
+{
+	const u8 data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+#ifdef __BIG_ENDIAN
+	const char *expected = "0001020304050607 08";
+	static const char expected_raw[] = "0001020304050607 080";
+#else
+	const char *expected = "0706050403020100 09";
+	static const char expected_raw[] = "0706050403020100 090";
+#endif
+	struct seq_buf s;
+	char buf[20];
+
+	/* Stale bytes after the data must not show up in the string. */
+	memset(buf, 'X', sizeof(buf));
+	seq_buf_init(&s, buf, sizeof(buf));
+	KUNIT_EXPECT_EQ(test, seq_buf_putmem_hex(&s, data, sizeof(data)), -1);
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
+
+	/* Before seq_buf_str() writes the terminator over the last byte. */
+	KUNIT_EXPECT_MEMEQ(test, buf, expected_raw, sizeof(buf));
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected);
+}
+
 /*
  * Counters for the console that seq_buf_do_printk_test() registers while it
  * runs. Only records carrying the marker are counted, so unrelated kernel
@@ -401,6 +479,9 @@ static struct kunit_case seq_buf_test_cases[] = {
 	KUNIT_CASE(seq_buf_get_buf_commit_test),
 	KUNIT_CASE(seq_buf_putmem_hex_test),
 	KUNIT_CASE(seq_buf_putmem_hex_overflow_test),
+	KUNIT_CASE(seq_buf_puts_partial_overflow_test),
+	KUNIT_CASE(seq_buf_putmem_partial_overflow_test),
+	KUNIT_CASE(seq_buf_putmem_hex_partial_overflow_test),
 	KUNIT_CASE(seq_buf_do_printk_test),
 	{}
 };
-- 
2.55.0


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

* [PATCH v4 04/11] seq_buf: Clear what a writer did not claim when a seq_buf overflows
  2026-10-03  3:59 [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Kees Cook
                   ` (2 preceding siblings ...)
  2026-10-03  3:59 ` [PATCH v4 03/11] seq_buf: Copy what fits when seq_buf_puts() and seq_buf_putmem() overflow Kees Cook
@ 2026-10-03  3:59 ` Kees Cook
  2026-10-03  4:50   ` bot+bpf-ci
  2026-10-03  3:59 ` [PATCH v4 05/11] seq_buf: Add seq_buf_strlen() Kees Cook
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 19+ messages in thread
From: Kees Cook @ 2026-10-03  3:59 UTC (permalink / raw)
  To: Bill Wendling
  Cc: Kees Cook, Matthew Wilcox (Oracle),
	Andrew Morton, Andy Shevchenko, David Gow, Jiri Kosina,
	Petr Mladek, Shuvam Pandey, Steven Rostedt, Jonathan Corbet,
	Sergey Senozhatsky, Günther Noack, Mickaël Salaün,
	Masami Hiramatsu, Mathieu Desnoyers, Alexei Starovoitov,
	Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jiri Olsa, Emil Tsalapatis, Ihor Solodrai,
	Christophe Leroy (CS GROUP),
	Uwe Kleine-König, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Shivaprasad G Bhat, Thorsten Blum,
	Alison Schofield, Dave Jiang, Greg Kroah-Hartman, Guangshuo Li,
	Ira Weiny, Uwe Kleine-König, Vishal Verma, Randy Dunlap,
	Shuah Khan, linux-kernel, bpf, linux-security-module,
	linux-trace-kernel, linuxppc-dev, nvdimm, linux-doc,
	linux-hardening

Using seq_buf_set_overflow() would leave the bytes between "len"
and "size" untouched, so if seq_buf_str() is used on an overflowed
seq_buf, those bytes may be exposed. For any paths that don't claim
partially written bytes, by setting "len = size" before calling
seq_buf_set_overflow(), wipe the unclaimed bytes. The seq_buf_puts()
and related APIs already claim those bytes now, so only the unclaimed
cases remain. A specific example of this was seq_buf_path() which uses
d_path() and would write to the tail before discovering it was out
of space, and would correctly mark a seq_buf as overflowed, but the
path fragment would be left over.

Clear from len to the end of the buffer in seq_buf_set_overflow(), which
every overflow goes through, including seq_buf_commit() with a negative
count.

Add a test that fills a seq_buf, leaves it too little room for a path, and
checks that nothing of the path is left in the buffer. The tests run before
anything writable is mounted, so it takes its file from shmem.

seq_buf_path() was never exported, unlike the other writers, so the
test failed to link as a module. Export it.

Tests passed under qemu on ARCH=x86_64 with GCC 16.2.0 and CONFIG_KASAN=y,
and on big-endian ARCH=s390 with GCC s390x-linux-gnu 16.2.0, and the
test builds as a module (CONFIG_SEQ_BUF_KUNIT_TEST=m).

Assisted-by: LLM
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Kees Cook <kees@kernel.org>
---
 include/linux/seq_buf.h   |  8 +++++--
 lib/seq_buf.c             |  5 +++++
 lib/tests/seq_buf_kunit.c | 45 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
index 77e76e283370..0c0a0db04b09 100644
--- a/include/linux/seq_buf.h
+++ b/include/linux/seq_buf.h
@@ -5,6 +5,7 @@
 #include <linux/bug.h>
 #include <linux/minmax.h>
 #include <linux/seq_file.h>
+#include <linux/string.h>
 #include <linux/types.h>
 
 /*
@@ -58,12 +59,15 @@ seq_buf_has_overflowed(struct seq_buf *s)
 /*
  * Mark @s as overflowed, which discards the length of what it holds. The
  * bytes up to its last one are the string from then on, as that is where
- * seq_buf_str() terminates it, so a caller that could not fill the buffer
- * has to NUL them itself before calling this.
+ * seq_buf_str() terminates it, so clear whatever was not written: a writer
+ * sets len to how much it filled, and anything past that was never adopted.
  */
 static inline void
 seq_buf_set_overflow(struct seq_buf *s)
 {
+	if (s->len < s->size)
+		memset(s->buffer + s->len, 0, s->size - s->len);
+
 	s->len = s->size + 1;
 }
 
diff --git a/lib/seq_buf.c b/lib/seq_buf.c
index 60e9eadb3ef7..8da2e9447adf 100644
--- a/lib/seq_buf.c
+++ b/lib/seq_buf.c
@@ -76,6 +76,8 @@ int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)
 			s->len += len;
 			return 0;
 		}
+		/* vsnprintf() wrote as much as fits, so none of it is stale */
+		s->len = s->size;
 	}
 	seq_buf_set_overflow(s);
 	return -1;
@@ -164,6 +166,8 @@ int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary)
 			s->len += ret;
 			return 0;
 		}
+		/* bstr_printf() wrote as much as fits, so none of it is stale */
+		s->len = s->size;
 	}
 	seq_buf_set_overflow(s);
 	return -1;
@@ -343,6 +347,7 @@ int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
 
 	return res;
 }
+EXPORT_SYMBOL_GPL(seq_buf_path);
 
 /**
  * seq_buf_to_user - copy the sequence buffer to user space
diff --git a/lib/tests/seq_buf_kunit.c b/lib/tests/seq_buf_kunit.c
index 046614100c77..d5b819fc0ff3 100644
--- a/lib/tests/seq_buf_kunit.c
+++ b/lib/tests/seq_buf_kunit.c
@@ -6,6 +6,9 @@
  */
 
 #include <kunit/test.h>
+#include <linux/fs.h>
+#include <linux/seq_buf.h>
+#include <linux/shmem_fs.h>
 #include <linux/console.h>
 #include <linux/seq_buf.h>
 #include <linux/string.h>
@@ -466,6 +469,47 @@ static void seq_buf_do_printk_test(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, seq_buf_printk_empty, 0);
 }
 
+/* Long enough that it cannot fit in the room the test leaves for it. */
+#define SEQ_BUF_TEST_PATH	"/seq_buf_kunit_path_name"
+
+static void seq_buf_path_overflow_test(struct kunit *test)
+{
+	DECLARE_SEQ_BUF(s, 32);
+	const char *expected = "keep:xxxxxxxxxxxxxxxxxxxxxxx";
+	struct file *file;
+	size_t len;
+	int i;
+
+	/*
+	 * The tests run before anything writable is mounted, so take the file
+	 * whose path gets printed from shmem, which needs no mount of its own.
+	 */
+	file = shmem_file_setup(SEQ_BUF_TEST_PATH, 0, EMPTY_VMA_FLAGS);
+	if (IS_ERR(file))
+		kunit_skip(test, "cannot create a file to print the path of");
+
+	/* Leave less room than the path needs, so d_path() cannot fit it. */
+	seq_buf_puts(&s, "keep:");
+	len = seq_buf_used(&s);
+	for (i = len; i < 28; i++)
+		seq_buf_putc(&s, 'x');
+
+	KUNIT_EXPECT_EQ(test, seq_buf_path(&s, &file->f_path, "\n"), -1);
+	fput(file);
+
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
+
+	/*
+	 * d_path() keeps as much of the path as fits when it does not fit
+	 * whole, and seq_buf_str() would hand out that fragment, as it ends
+	 * the string at the last byte of an overflowed buffer.
+	 */
+	for (i = 28; i < 32; i++)
+		KUNIT_EXPECT_EQ_MSG(test, s.buffer[i], '\0',
+				    "byte %d past the data is not cleared", i);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected);
+}
+
 static struct kunit_case seq_buf_test_cases[] = {
 	KUNIT_CASE(seq_buf_init_test),
 	KUNIT_CASE(seq_buf_declare_test),
@@ -482,6 +526,7 @@ static struct kunit_case seq_buf_test_cases[] = {
 	KUNIT_CASE(seq_buf_puts_partial_overflow_test),
 	KUNIT_CASE(seq_buf_putmem_partial_overflow_test),
 	KUNIT_CASE(seq_buf_putmem_hex_partial_overflow_test),
+	KUNIT_CASE(seq_buf_path_overflow_test),
 	KUNIT_CASE(seq_buf_do_printk_test),
 	{}
 };
-- 
2.55.0


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

* [PATCH v4 05/11] seq_buf: Add seq_buf_strlen()
  2026-10-03  3:59 [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Kees Cook
                   ` (3 preceding siblings ...)
  2026-10-03  3:59 ` [PATCH v4 04/11] seq_buf: Clear what a writer did not claim when a seq_buf overflows Kees Cook
@ 2026-10-03  3:59 ` Kees Cook
  2026-10-03 15:36   ` Andy Shevchenko
  2026-10-03  3:59 ` [PATCH v4 06/11] seq_buf: Add seq_buf_terminate() Kees Cook
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 19+ messages in thread
From: Kees Cook @ 2026-10-03  3:59 UTC (permalink / raw)
  To: Bill Wendling
  Cc: Kees Cook, Matthew Wilcox (Oracle),
	Andrew Morton, Andy Shevchenko, David Gow, Petr Mladek,
	Shuvam Pandey, Steven Rostedt, Jonathan Corbet,
	Sergey Senozhatsky, Günther Noack, Mickaël Salaün,
	Masami Hiramatsu, Mathieu Desnoyers, Jiri Kosina,
	Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
	Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
	Ihor Solodrai, Christophe Leroy (CS GROUP),
	Uwe Kleine-König, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Shivaprasad G Bhat, Thorsten Blum,
	Alison Schofield, Dave Jiang, Greg Kroah-Hartman, Guangshuo Li,
	Ira Weiny, Uwe Kleine-König, Vishal Verma, Randy Dunlap,
	Shuah Khan, linux-kernel, bpf, linux-security-module,
	linux-trace-kernel, linuxppc-dev, nvdimm, linux-doc,
	linux-hardening

Several strlcat() call sites being converted to seq_buf need behavior
seq_buf doesn't currently provide. The return from seq_buf_used() is
not the length of the string in a seq_buf. Once the buffer is full or
has overflowed it returns the buffer size, which counts the byte that
seq_buf_str() replaces with the NUL, so a caller that needs the string
and its length has to call seq_buf_str() and then walk the string with
strlen().

Move the termination out of seq_buf_str() into a helper that returns
where it put the NUL, and add seq_buf_strlen(), which terminates the
buffer in the same way and returns that offset.

As discussed in review, don't add WARN_ON() for seq_buf_strlen() and
drop it from seq_buf_str().

Add tests comparing seq_buf_strlen() against strlen() of seq_buf_str()
for empty, appended, truncated, exactly full, and overflowed buffers,
checking that seq_buf_strlen() alone terminates a full buffer, and
checking that a zero-sized seq_buf reports an empty string from both
accessors without touching the buffer.

Tests passed under qemu on ARCH=x86_64 with GCC 16.2.0 and CONFIG_KASAN=y,
and on big-endian ARCH=s390 with GCC s390x-linux-gnu 16.2.0.

Assisted-by: LLM
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Kees Cook <kees@kernel.org>
---
 include/linux/seq_buf.h   |  65 +++++++++++++++++--
 lib/tests/seq_buf_kunit.c | 129 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 188 insertions(+), 6 deletions(-)

diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
index 0c0a0db04b09..87ccc62f1c62 100644
--- a/include/linux/seq_buf.h
+++ b/include/linux/seq_buf.h
@@ -89,6 +89,27 @@ static inline unsigned int seq_buf_used(struct seq_buf *s)
 	return min(s->len, s->size);
 }
 
+/*
+ * NUL-terminate the buffer in @s: directly after the data when there is
+ * room for it, otherwise in the last byte of the buffer. @s->size must not
+ * be zero.
+ *
+ * Returns: the offset of the NUL.
+ */
+static inline size_t __seq_buf_terminate(struct seq_buf *s)
+{
+	size_t end;
+
+	if (seq_buf_buffer_left(s))
+		end = s->len;
+	else
+		end = s->size - 1;
+
+	s->buffer[end] = 0;
+
+	return end;
+}
+
 /**
  * seq_buf_str - get NUL-terminated C string from seq_buf
  * @s: the seq_buf handle
@@ -98,7 +119,12 @@ static inline unsigned int seq_buf_used(struct seq_buf *s)
  *
  * Note, if this is called when the buffer has overflowed, then
  * the last byte of the buffer is zeroed, and the len will still
- * point passed it.
+ * point passed it. The same happens when the buffer is exactly
+ * full: the NUL takes the place of the last byte written, which is
+ * lost, though seq_buf_used() still counts it.
+ *
+ * A zero-sized seq_buf has nowhere to put a NUL, so the empty string
+ * is returned instead of writing to @s->buffer.
  *
  * After this function is called, s->buffer is safe to use
  * in string operations.
@@ -107,17 +133,44 @@ static inline unsigned int seq_buf_used(struct seq_buf *s)
  */
 static inline const char *seq_buf_str(struct seq_buf *s)
 {
-	if (WARN_ON(s->size == 0))
+	if (s->size == 0)
 		return "";
 
-	if (seq_buf_buffer_left(s))
-		s->buffer[s->len] = 0;
-	else
-		s->buffer[s->size - 1] = 0;
+	__seq_buf_terminate(s);
 
 	return s->buffer;
 }
 
+/**
+ * seq_buf_strlen - get the length of the NUL-terminated C string in seq_buf
+ * @s: the seq_buf handle
+ *
+ * This makes sure that the buffer in @s is NUL-terminated, exactly as
+ * seq_buf_str() does, and returns the length of the resulting string
+ * without walking it. Unlike seq_buf_used(), this does not count the byte
+ * given up to the NUL when the buffer is full or has overflowed. When the
+ * buffer is exactly full, that byte is the last one written, and calling
+ * either function loses it.
+ *
+ * A zero-sized seq_buf holds no string, so 0 is returned without writing
+ * to @s->buffer, matching what seq_buf_str() returns for one.
+ *
+ * After this function is called, s->buffer is safe to use
+ * in string operations.
+ *
+ * Returns: the offset of the NUL that terminates @s->buffer. That is the
+ * length of the string unless an earlier NUL is in the way, either one the
+ * data written to @s carried itself, or one seq_buf_set_overflow() left
+ * behind when it cleared what no writer had claimed.
+ */
+static inline size_t seq_buf_strlen(struct seq_buf *s)
+{
+	if (s->size == 0)
+		return 0;
+
+	return __seq_buf_terminate(s);
+}
+
 /**
  * seq_buf_get_buf - get buffer to write arbitrary data to
  * @s: the seq_buf handle
diff --git a/lib/tests/seq_buf_kunit.c b/lib/tests/seq_buf_kunit.c
index d5b819fc0ff3..569ab3d96f10 100644
--- a/lib/tests/seq_buf_kunit.c
+++ b/lib/tests/seq_buf_kunit.c
@@ -26,6 +26,7 @@ static void seq_buf_init_test(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, seq_buf_buffer_left(&s), 32);
 	KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 0);
 	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "");
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 0);
 }
 
 static void seq_buf_declare_test(struct kunit *test)
@@ -510,6 +511,128 @@ static void seq_buf_path_overflow_test(struct kunit *test)
 	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected);
 }
 
+static void seq_buf_strlen_test(struct kunit *test)
+{
+	DECLARE_SEQ_BUF(s, 16);
+
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 0);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "");
+
+	seq_buf_puts(&s, "hello");
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 5);
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), strlen(seq_buf_str(&s)));
+
+	seq_buf_printf(&s, " %s", "world");
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 11);
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), strlen(seq_buf_str(&s)));
+}
+
+static void seq_buf_strlen_printf_overflow_test(struct kunit *test)
+{
+	DECLARE_SEQ_BUF(s, 16);
+	DECLARE_SEQ_BUF(t, 8);
+
+	seq_buf_printf(&s, "%s", "1234567890abcdefghij");
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
+	KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 16);
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 15);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "1234567890abcde");
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), strlen(seq_buf_str(&s)));
+
+	/* Output one byte too long for the NUL. */
+	seq_buf_printf(&t, "%s", "12345678");
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&t));
+	KUNIT_EXPECT_EQ(test, seq_buf_used(&t), 8);
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&t), 7);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&t), "1234567");
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&t), strlen(seq_buf_str(&t)));
+}
+
+static void seq_buf_strlen_full_test(struct kunit *test)
+{
+	DECLARE_SEQ_BUF(s, 4);
+	DECLARE_SEQ_BUF(t, 8);
+	char *buf;
+	size_t len;
+
+	/* Filled exactly, with no room left for a NUL, but not overflowed. */
+	seq_buf_putc(&s, 'a');
+	seq_buf_putc(&s, 'b');
+	seq_buf_putc(&s, 'c');
+	seq_buf_putc(&s, 'd');
+	KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&s));
+	KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 4);
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 3);
+	/* seq_buf_strlen() terminates the buffer by itself. */
+	KUNIT_EXPECT_EQ(test, s.buffer[3], '\0');
+	KUNIT_EXPECT_EQ(test, strnlen(s.buffer, s.size), 3);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "abc");
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), strlen(seq_buf_str(&s)));
+
+	/* A printf into a full buffer writes nothing. */
+	KUNIT_EXPECT_EQ(test, seq_buf_printf(&s, "%s", "x"), -1);
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 3);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "abc");
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), strlen(seq_buf_str(&s)));
+
+	len = seq_buf_get_buf(&t, &buf);
+	KUNIT_ASSERT_EQ(test, len, 8);
+	memset(buf, 'z', len);
+	seq_buf_commit(&t, len);
+	KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&t));
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&t), 7);
+	KUNIT_EXPECT_EQ(test, t.buffer[7], '\0');
+	KUNIT_EXPECT_EQ(test, strnlen(t.buffer, t.size), 7);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&t), "zzzzzzz");
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&t), strlen(seq_buf_str(&t)));
+}
+
+static void seq_buf_strlen_puts_overflow_test(struct kunit *test)
+{
+	DECLARE_SEQ_BUF(s, 16);
+
+	/* A puts that does not fit copies as much as fits. */
+	seq_buf_puts(&s, "hello");
+	KUNIT_EXPECT_EQ(test, seq_buf_puts(&s, " this does not fit"), -1);
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 15);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hello this does");
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), strlen(seq_buf_str(&s)));
+}
+
+static void seq_buf_strlen_embedded_nul_test(struct kunit *test)
+{
+	static const char data[] = "ab\0cd";
+	DECLARE_SEQ_BUF(s, 16);
+
+	/*
+	 * seq_buf_strlen() reports where it put the terminator, not where
+	 * the first NUL is, so data carrying a NUL of its own makes the two
+	 * disagree. That is expected, and is what the documented caveat is
+	 * about.
+	 */
+	seq_buf_putmem(&s, data, sizeof(data) - 1);
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 5);
+	KUNIT_EXPECT_EQ(test, strlen(seq_buf_str(&s)), 2);
+}
+
+static void seq_buf_strlen_zero_size_test(struct kunit *test)
+{
+	char buf[] = "untouched";
+	struct seq_buf s;
+
+	/*
+	 * A zero-sized seq_buf has nowhere to put a terminator. Both
+	 * accessors report an empty string and leave the buffer alone
+	 * rather than writing outside it.
+	 */
+	seq_buf_init(&s, buf, 0);
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 0);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "");
+	KUNIT_EXPECT_STREQ(test, buf, "untouched");
+}
+
 static struct kunit_case seq_buf_test_cases[] = {
 	KUNIT_CASE(seq_buf_init_test),
 	KUNIT_CASE(seq_buf_declare_test),
@@ -527,6 +650,12 @@ static struct kunit_case seq_buf_test_cases[] = {
 	KUNIT_CASE(seq_buf_putmem_partial_overflow_test),
 	KUNIT_CASE(seq_buf_putmem_hex_partial_overflow_test),
 	KUNIT_CASE(seq_buf_path_overflow_test),
+	KUNIT_CASE(seq_buf_strlen_test),
+	KUNIT_CASE(seq_buf_strlen_printf_overflow_test),
+	KUNIT_CASE(seq_buf_strlen_full_test),
+	KUNIT_CASE(seq_buf_strlen_puts_overflow_test),
+	KUNIT_CASE(seq_buf_strlen_embedded_nul_test),
+	KUNIT_CASE(seq_buf_strlen_zero_size_test),
 	KUNIT_CASE(seq_buf_do_printk_test),
 	{}
 };
-- 
2.55.0


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

* [PATCH v4 06/11] seq_buf: Add seq_buf_terminate()
  2026-10-03  3:59 [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Kees Cook
                   ` (4 preceding siblings ...)
  2026-10-03  3:59 ` [PATCH v4 05/11] seq_buf: Add seq_buf_strlen() Kees Cook
@ 2026-10-03  3:59 ` Kees Cook
  2026-10-03  3:59 ` [PATCH v4 07/11] bpf: Remove dead newline stripping from format_disasm_line() Kees Cook
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Kees Cook @ 2026-10-03  3:59 UTC (permalink / raw)
  To: Bill Wendling
  Cc: Kees Cook, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
	Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
	Ihor Solodrai, Steven Rostedt, Masami Hiramatsu,
	Mathieu Desnoyers, Andy Shevchenko, Petr Mladek,
	Matthew Wilcox (Oracle),
	Shuvam Pandey, David Gow, Andrew Morton, bpf, linux-trace-kernel,
	Jonathan Corbet, Sergey Senozhatsky, Günther Noack,
	Mickaël Salaün, Jiri Kosina,
	Christophe Leroy (CS GROUP),
	Uwe Kleine-König, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Shivaprasad G Bhat, Thorsten Blum,
	Alison Schofield, Dave Jiang, Greg Kroah-Hartman, Guangshuo Li,
	Ira Weiny, Uwe Kleine-König, Vishal Verma, Randy Dunlap,
	Shuah Khan, linux-kernel, linux-security-module, linuxppc-dev,
	nvdimm, linux-doc, linux-hardening

Seven callers call seq_buf_str() only to NUL-terminate the buffer,
discarding the returned pointer. Two of them need a comment to say so.

Add seq_buf_terminate(), wrapping the __seq_buf_terminate() helper that
seq_buf_str() and seq_buf_strlen() already use, and convert those
callers. It returns void: returning the offset would just be
seq_buf_strlen() under another name. A zero-sized seq_buf is left
untouched, as in the other accessors.

Add tests for the three cases: room for the NUL after the data, an
overflowed buffer where it lands in the last byte, and a zero-sized
buffer that must not be written to.

Build tested ARCH=x86_64 defconfig with GCC 16.2.0, plus
CONFIG_HIST_TRIGGERS=y and CONFIG_BPF_SYSCALL=y to reach the converted
call sites in kernel/trace/trace_events_hist.c and
kernel/bpf/diagnostics.c. Tests run 24/24 passing on ARCH=um.

Assisted-by: LLM
Signed-off-by: Kees Cook <kees@kernel.org>
---
 include/linux/seq_buf.h          | 23 +++++++++++++++++++++++
 kernel/bpf/diagnostics.c         |  6 +++---
 kernel/trace/trace_events.c      |  4 ++--
 kernel/trace/trace_events_hist.c |  6 ++----
 lib/tests/seq_buf_kunit.c        | 28 ++++++++++++++++++++++++++++
 5 files changed, 58 insertions(+), 9 deletions(-)

diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
index 87ccc62f1c62..195e612a212a 100644
--- a/include/linux/seq_buf.h
+++ b/include/linux/seq_buf.h
@@ -171,6 +171,29 @@ static inline size_t seq_buf_strlen(struct seq_buf *s)
 	return __seq_buf_terminate(s);
 }
 
+/**
+ * seq_buf_terminate - NUL-terminate the string in a seq_buf
+ * @s: the seq_buf handle
+ *
+ * Terminate @s->buffer exactly as seq_buf_str() and seq_buf_strlen() do,
+ * for callers that want neither the pointer nor the length and only need
+ * the buffer to be safe to read as a C string. A zero-sized seq_buf has
+ * nowhere to put a NUL and is left untouched.
+ *
+ * Nothing is returned on purpose: a caller that wants the length should
+ * use seq_buf_strlen(), which says so.
+ *
+ * After this function is called, s->buffer is safe to use
+ * in string operations.
+ */
+static inline void seq_buf_terminate(struct seq_buf *s)
+{
+	if (s->size == 0)
+		return;
+
+	__seq_buf_terminate(s);
+}
+
 /**
  * seq_buf_get_buf - get buffer to write arbitrary data to
  * @s: the seq_buf handle
diff --git a/kernel/bpf/diagnostics.c b/kernel/bpf/diagnostics.c
index 0abbbe177e31..594cf3c8b74c 100644
--- a/kernel/bpf/diagnostics.c
+++ b/kernel/bpf/diagnostics.c
@@ -351,7 +351,7 @@ static void diag_fmt_restore(struct bpf_verifier_env *env, struct diag_fmt_mark
 
 	if (mark.chunk) {
 		mark.chunk->seq.len = mark.len;
-		seq_buf_str(&mark.chunk->seq);
+		seq_buf_terminate(&mark.chunk->seq);
 	}
 }
 
@@ -631,11 +631,11 @@ static void format_disasm_line(struct bpf_verifier_env *env, int insn_idx,
 		return;
 
 	print_bpf_insn(&cbs, insn, env->allow_ptr_leaks);
-	seq_buf_str(&ctx.seq);
+	seq_buf_terminate(&ctx.seq);
 	ctx.seq.len = strnlen(line->text, sizeof(line->text));
 	while (ctx.seq.len && line->text[ctx.seq.len - 1] == '\n')
 		seq_buf_pop(&ctx.seq);
-	seq_buf_str(&ctx.seq);
+	seq_buf_terminate(&ctx.seq);
 
 	line->valid = true;
 }
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 9dbc2441763b..39bb391546de 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -4906,7 +4906,7 @@ static __init int event_trace_enable(void)
 	 */
 	__trace_early_add_events(tr);
 
-	seq_buf_str(&bootup_event_seq);
+	seq_buf_terminate(&bootup_event_seq);
 	early_enable_events(tr, bootup_event_buf, false);
 
 	trace_printk_start_comm();
@@ -4935,7 +4935,7 @@ static __init int event_trace_enable_again(void)
 	if (!tr)
 		return -ENODEV;
 
-	seq_buf_str(&bootup_event_seq);
+	seq_buf_terminate(&bootup_event_seq);
 	early_enable_events(tr, bootup_event_buf, true);
 
 	return 0;
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 963e0d6b61fd..57bd1cd5c657 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -2988,8 +2988,7 @@ find_synthetic_field_var(struct hist_trigger_data *target_hist_data,
 	seq_buf_init(&s, synthetic_name, MAX_FILTER_STR_VAL);
 	seq_buf_printf(&s, "synthetic_%s", field_name);
 
-	/* Terminate synthetic_name with a NUL. */
-	seq_buf_str(&s);
+	seq_buf_terminate(&s);
 
 	if (seq_buf_has_overflowed(&s)) {
 		kfree(synthetic_name);
@@ -3106,8 +3105,7 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data,
 	if (saved_filter)
 		seq_buf_printf(&s, " if %s", saved_filter);
 
-	/* Terminate cmd with a NUL. */
-	seq_buf_str(&s);
+	seq_buf_terminate(&s);
 
 	if (seq_buf_has_overflowed(&s)) {
 		kfree(cmd);
diff --git a/lib/tests/seq_buf_kunit.c b/lib/tests/seq_buf_kunit.c
index 569ab3d96f10..b6fc7b784859 100644
--- a/lib/tests/seq_buf_kunit.c
+++ b/lib/tests/seq_buf_kunit.c
@@ -633,6 +633,33 @@ static void seq_buf_strlen_zero_size_test(struct kunit *test)
 	KUNIT_EXPECT_STREQ(test, buf, "untouched");
 }
 
+static void seq_buf_terminate_test(struct kunit *test)
+{
+	char buf[16];
+	struct seq_buf s;
+
+	/* Terminates directly after the data when there is room. */
+	memset(buf, 'z', sizeof(buf));
+	seq_buf_init(&s, buf, sizeof(buf));
+	seq_buf_puts(&s, "ab");
+	seq_buf_terminate(&s);
+	KUNIT_EXPECT_STREQ(test, buf, "ab");
+
+	/* Terminates in the last byte once the buffer has overflowed. */
+	memset(buf, 'z', sizeof(buf));
+	seq_buf_init(&s, buf, 4);
+	seq_buf_puts(&s, "abcdef");
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
+	seq_buf_terminate(&s);
+	KUNIT_EXPECT_STREQ(test, buf, "abc");
+
+	/* A zero-sized seq_buf is left alone. */
+	strscpy(buf, "untouched", sizeof(buf));
+	seq_buf_init(&s, buf, 0);
+	seq_buf_terminate(&s);
+	KUNIT_EXPECT_STREQ(test, buf, "untouched");
+}
+
 static struct kunit_case seq_buf_test_cases[] = {
 	KUNIT_CASE(seq_buf_init_test),
 	KUNIT_CASE(seq_buf_declare_test),
@@ -656,6 +683,7 @@ static struct kunit_case seq_buf_test_cases[] = {
 	KUNIT_CASE(seq_buf_strlen_puts_overflow_test),
 	KUNIT_CASE(seq_buf_strlen_embedded_nul_test),
 	KUNIT_CASE(seq_buf_strlen_zero_size_test),
+	KUNIT_CASE(seq_buf_terminate_test),
 	KUNIT_CASE(seq_buf_do_printk_test),
 	{}
 };
-- 
2.55.0


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

* [PATCH v4 07/11] bpf: Remove dead newline stripping from format_disasm_line()
  2026-10-03  3:59 [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Kees Cook
                   ` (5 preceding siblings ...)
  2026-10-03  3:59 ` [PATCH v4 06/11] seq_buf: Add seq_buf_terminate() Kees Cook
@ 2026-10-03  3:59 ` Kees Cook
  2026-10-03  3:59 ` [PATCH v4 08/11] seq_buf: Add seq_buf_init_append() Kees Cook
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Kees Cook @ 2026-10-03  3:59 UTC (permalink / raw)
  To: Bill Wendling
  Cc: Kees Cook, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
	Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
	Ihor Solodrai, bpf, Jonathan Corbet, Andrew Morton, David Gow,
	Petr Mladek, Sergey Senozhatsky, Shuvam Pandey, Steven Rostedt,
	Günther Noack, Matthew Wilcox (Oracle),
	Mickaël Salaün, Andy Shevchenko, Masami Hiramatsu,
	Mathieu Desnoyers, Jiri Kosina, Christophe Leroy (CS GROUP),
	Uwe Kleine-König, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Shivaprasad G Bhat, Thorsten Blum,
	Alison Schofield, Dave Jiang, Greg Kroah-Hartman, Guangshuo Li,
	Ira Weiny, Uwe Kleine-König, Vishal Verma, Randy Dunlap,
	Shuah Khan, linux-kernel, linux-security-module,
	linux-trace-kernel, linuxppc-dev, nvdimm, linux-doc,
	linux-hardening

format_disasm_line() strips trailing newlines from the disassembly it
stores, as diag_print_insn_context() adds its own. Since
commit 483a1bb0b6cf8 ("bpf: Do not print a newline after disassembly in
bpf_verbose_insn()"), print_bpf_insn() does not emit one, so there is
nothing left to strip.

The strnlen() resync of the seq_buf length only existed so the loop
could index the buffer safely after an overflow, and the second
termination only restored the NUL that seq_buf_pop() does not write.
Remove all of it, keeping a single seq_buf_terminate().

Build tested ARCH=x86_64 defconfig with GCC 16.2.0 and
CONFIG_BPF_SYSCALL=y.

Assisted-by: LLM
Signed-off-by: Kees Cook <kees@kernel.org>
---
 kernel/bpf/diagnostics.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/kernel/bpf/diagnostics.c b/kernel/bpf/diagnostics.c
index 594cf3c8b74c..8f64bfd9afee 100644
--- a/kernel/bpf/diagnostics.c
+++ b/kernel/bpf/diagnostics.c
@@ -632,10 +632,6 @@ static void format_disasm_line(struct bpf_verifier_env *env, int insn_idx,
 
 	print_bpf_insn(&cbs, insn, env->allow_ptr_leaks);
 	seq_buf_terminate(&ctx.seq);
-	ctx.seq.len = strnlen(line->text, sizeof(line->text));
-	while (ctx.seq.len && line->text[ctx.seq.len - 1] == '\n')
-		seq_buf_pop(&ctx.seq);
-	seq_buf_terminate(&ctx.seq);
 
 	line->valid = true;
 }
-- 
2.55.0


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

* [PATCH v4 08/11] seq_buf: Add seq_buf_init_append()
  2026-10-03  3:59 [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Kees Cook
                   ` (6 preceding siblings ...)
  2026-10-03  3:59 ` [PATCH v4 07/11] bpf: Remove dead newline stripping from format_disasm_line() Kees Cook
@ 2026-10-03  3:59 ` Kees Cook
  2026-10-03  4:33   ` bot+bpf-ci
  2026-10-03  3:59 ` [PATCH v4 09/11] powerpc/papr_scm: Return the string length from the sysfs show functions Kees Cook
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 19+ messages in thread
From: Kees Cook @ 2026-10-03  3:59 UTC (permalink / raw)
  To: Bill Wendling
  Cc: Kees Cook, Matthew Wilcox (Oracle),
	Andrew Morton, Andy Shevchenko, David Gow, Petr Mladek,
	Shuvam Pandey, Steven Rostedt, Jonathan Corbet,
	Sergey Senozhatsky, Günther Noack, Mickaël Salaün,
	Masami Hiramatsu, Mathieu Desnoyers, Jiri Kosina,
	Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
	Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
	Ihor Solodrai, Christophe Leroy (CS GROUP),
	Uwe Kleine-König, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Shivaprasad G Bhat, Thorsten Blum,
	Alison Schofield, Dave Jiang, Greg Kroah-Hartman, Guangshuo Li,
	Ira Weiny, Uwe Kleine-König, Vishal Verma, Randy Dunlap,
	Shuah Khan, linux-kernel, bpf, linux-security-module,
	linux-trace-kernel, linuxppc-dev, nvdimm, linux-doc,
	linux-hardening

From: Bill Wendling <morbo@google.com>

Several strlcat() call sites being converted to seq_buf need behavior
seq_buf doesn't currently provide. The normal seq_buf_init() always
sets the new buffer size to 0 via seq_buf_clear(). Code migrating from
strlcat(buf, ...), which appends to whatever buf already contains,
can't use seq_buf_init() without discarding that existing content. Add
seq_buf_init_append(), which preserves the existing contents and positions
the seq_buf to append after it. A buffer with no NUL within its size
starts out overflowed, as strlcat() treats it as already truncated.
Add KUnit tests for behavior coverage.

Tests passed under qemu on ARCH=x86_64 with GCC 16.2.0 and CONFIG_KASAN=y,
and on big-endian ARCH=s390 with GCC s390x-linux-gnu 16.2.0.

Assisted-by: LLM
Signed-off-by: Bill Wendling <morbo@google.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Co-developed-by: Kees Cook <kees@kernel.org>
Signed-off-by: Kees Cook <kees@kernel.org>
---
 include/linux/seq_buf.h   | 25 +++++++++++++++
 lib/tests/seq_buf_kunit.c | 67 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 92 insertions(+)

diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
index 195e612a212a..50b1e78eeea6 100644
--- a/include/linux/seq_buf.h
+++ b/include/linux/seq_buf.h
@@ -71,6 +71,31 @@ seq_buf_set_overflow(struct seq_buf *s)
 	s->len = s->size + 1;
 }
 
+/**
+ * seq_buf_init_append - initialize a seq_buf over a buffer that may
+ *			 already hold NUL-terminated content
+ * @s: the seq_buf handle
+ * @buf: pointer to the (possibly non-empty) buffer
+ * @size: total size of @buf
+ *
+ * Unlike seq_buf_init(), which always starts @buf at len=0, this
+ * preserves whatever NUL-terminated content @buf already holds and
+ * positions @s to append after it. Useful for converting code that used
+ * to append to an existing buffer with strlcat()/scnprintf() and friends.
+ *
+ * If @buf holds no NUL within @size, @s starts out overflowed, as
+ * strlcat() treats such a buffer as already truncated.
+ */
+static inline void
+seq_buf_init_append(struct seq_buf *s, char *buf, unsigned int size)
+{
+	s->buffer = buf;
+	s->size = size;
+	s->len = strnlen(buf, size);
+	if (s->len == size)
+		seq_buf_set_overflow(s);
+}
+
 /*
  * How much buffer is left on the seq_buf?
  */
diff --git a/lib/tests/seq_buf_kunit.c b/lib/tests/seq_buf_kunit.c
index b6fc7b784859..170524146892 100644
--- a/lib/tests/seq_buf_kunit.c
+++ b/lib/tests/seq_buf_kunit.c
@@ -29,6 +29,72 @@ static void seq_buf_init_test(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 0);
 }
 
+static void seq_buf_init_append_test(struct kunit *test)
+{
+	char buf[32] = "hello";
+	struct seq_buf s;
+
+	/* Initial string contents match. */
+	seq_buf_init_append(&s, buf, sizeof(buf));
+	KUNIT_EXPECT_EQ(test, s.size, 32);
+	KUNIT_EXPECT_EQ(test, s.len, 5);
+	KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&s));
+	KUNIT_EXPECT_EQ(test, seq_buf_buffer_left(&s), 32 - 5);
+	KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 5);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hello");
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 5);
+
+	/* Appending with space works. */
+	seq_buf_puts(&s, " world");
+	KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&s));
+	KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 11);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hello world");
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 11);
+
+	/* No truncation when space for NUL is present. */
+	seq_buf_init_append(&s, buf, 12);
+	KUNIT_EXPECT_EQ(test, s.size, 12);
+	KUNIT_EXPECT_EQ(test, s.len, 11);
+	KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&s));
+	KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 11);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hello world");
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 11);
+
+	/*
+	 * No NUL within the size: the content is already truncated, as
+	 * strlcat() would see it, so @s starts out overflowed. The content
+	 * stays, and appending adds nothing.
+	 */
+	seq_buf_init_append(&s, buf, 11);
+	KUNIT_EXPECT_EQ(test, s.size, 11);
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
+	KUNIT_EXPECT_EQ(test, seq_buf_buffer_left(&s), 0);
+	KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 11);
+	KUNIT_EXPECT_MEMEQ(test, buf, "hello world", 11);
+	seq_buf_puts(&s, "!");
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
+	KUNIT_EXPECT_MEMEQ(test, buf, "hello world", 11);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hello worl");
+	KUNIT_EXPECT_EQ(test, seq_buf_strlen(&s), 10);
+
+	/*
+	 * The size bounds the scan: the string runs past it, so there is
+	 * no NUL within the size either.
+	 */
+	seq_buf_init_append(&s, buf, 5);
+	KUNIT_EXPECT_EQ(test, s.size, 5);
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
+	KUNIT_EXPECT_EQ(test, seq_buf_buffer_left(&s), 0);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "hell");
+
+	/* With no room even for a NUL, @s is overflowed and @buf untouched. */
+	seq_buf_init_append(&s, buf, 0);
+	KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s));
+	KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 0);
+	KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), "");
+	KUNIT_EXPECT_EQ(test, buf[0], 'h');
+}
+
 static void seq_buf_declare_test(struct kunit *test)
 {
 	DECLARE_SEQ_BUF(s, 24);
@@ -662,6 +728,7 @@ static void seq_buf_terminate_test(struct kunit *test)
 
 static struct kunit_case seq_buf_test_cases[] = {
 	KUNIT_CASE(seq_buf_init_test),
+	KUNIT_CASE(seq_buf_init_append_test),
 	KUNIT_CASE(seq_buf_declare_test),
 	KUNIT_CASE(seq_buf_clear_test),
 	KUNIT_CASE(seq_buf_puts_test),
-- 
2.55.0


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

* [PATCH v4 09/11] powerpc/papr_scm: Return the string length from the sysfs show functions
  2026-10-03  3:59 [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Kees Cook
                   ` (7 preceding siblings ...)
  2026-10-03  3:59 ` [PATCH v4 08/11] seq_buf: Add seq_buf_init_append() Kees Cook
@ 2026-10-03  3:59 ` Kees Cook
  2026-10-03  3:59 ` [PATCH v4 10/11] nvdimm: ndtest: Return the string length from flags_show() Kees Cook
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Kees Cook @ 2026-10-03  3:59 UTC (permalink / raw)
  To: Bill Wendling
  Cc: Kees Cook, Christophe Leroy (CS GROUP),
	Uwe Kleine-König, linuxppc-dev, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Shivaprasad G Bhat,
	Thorsten Blum, Andy Shevchenko, Jonathan Corbet, Andrew Morton,
	David Gow, Petr Mladek, Sergey Senozhatsky, Shuvam Pandey,
	Steven Rostedt, Günther Noack, Matthew Wilcox (Oracle),
	Mickaël Salaün, Masami Hiramatsu, Mathieu Desnoyers,
	Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Alison Schofield, Dave Jiang,
	Greg Kroah-Hartman, Guangshuo Li, Ira Weiny,
	Uwe Kleine-König, Vishal Verma, Randy Dunlap, Shuah Khan,
	linux-kernel, bpf, linux-security-module, linux-trace-kernel,
	nvdimm, linux-doc, linux-hardening

perf_stats_show() and flags_show() build their output with a seq_buf
and return seq_buf_used(), which may include the trailing NUL byte
when the seq_buf has overflowed. Use seq_buf_strlen() instead. As
suggested in review, use it as well for flags_show()'s check whether
any flag was printed.

Build tested ARCH=powerpc ppc64_defconfig with GCC powerpc64-linux-gnu
16.2.0:
arch/powerpc/platforms/pseries/papr_scm.o

Assisted-by: LLM
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Kees Cook <kees@kernel.org>
---
 arch/powerpc/platforms/pseries/papr_scm.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c
index 75da96c08cdd..5c3bf06bb59b 100644
--- a/arch/powerpc/platforms/pseries/papr_scm.c
+++ b/arch/powerpc/platforms/pseries/papr_scm.c
@@ -1108,7 +1108,7 @@ static ssize_t perf_stats_show(struct device *dev,
 
 free_stats:
 	kfree(stats);
-	return rc ? rc : (ssize_t)seq_buf_used(&s);
+	return rc ?: (ssize_t)seq_buf_strlen(&s);
 }
 static DEVICE_ATTR_ADMIN_RO(perf_stats);
 
@@ -1147,10 +1147,10 @@ static ssize_t flags_show(struct device *dev,
 	if (health & PAPR_PMEM_SCRUBBED_AND_LOCKED)
 		seq_buf_printf(&s, "scrubbed locked ");
 
-	if (seq_buf_used(&s))
+	if (seq_buf_strlen(&s))
 		seq_buf_printf(&s, "\n");
 
-	return seq_buf_used(&s);
+	return seq_buf_strlen(&s);
 }
 DEVICE_ATTR_RO(flags);
 
-- 
2.55.0


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

* [PATCH v4 10/11] nvdimm: ndtest: Return the string length from flags_show()
  2026-10-03  3:59 [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Kees Cook
                   ` (8 preceding siblings ...)
  2026-10-03  3:59 ` [PATCH v4 09/11] powerpc/papr_scm: Return the string length from the sysfs show functions Kees Cook
@ 2026-10-03  3:59 ` Kees Cook
  2026-10-03  3:59 ` [PATCH v4 11/11] docs: core-api: Document the seq_buf API Kees Cook
  2026-10-03  6:32 ` [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Alexei Starovoitov
  11 siblings, 0 replies; 19+ messages in thread
From: Kees Cook @ 2026-10-03  3:59 UTC (permalink / raw)
  To: Bill Wendling
  Cc: Kees Cook, nvdimm, Alison Schofield, Dave Jiang,
	Greg Kroah-Hartman, Guangshuo Li, Ira Weiny,
	Uwe Kleine-König, Vishal Verma, Andy Shevchenko,
	Jonathan Corbet, Andrew Morton, David Gow, Petr Mladek,
	Sergey Senozhatsky, Shuvam Pandey, Steven Rostedt,
	Günther Noack, Matthew Wilcox (Oracle),
	Mickaël Salaün, Masami Hiramatsu, Mathieu Desnoyers,
	Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Christophe Leroy (CS GROUP),
	Uwe Kleine-König, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Shivaprasad G Bhat, Thorsten Blum, Randy Dunlap,
	Shuah Khan, linux-kernel, bpf, linux-security-module,
	linux-trace-kernel, linuxppc-dev, linux-doc, linux-hardening

flags_show() build their output with a seq_buf and return seq_buf_used(),
which may include the trailing NUL byte when the seq_buf has
overflowed. Use seq_buf_strlen() instead.

The flag names are far shorter than the PAGE_SIZE buffer sysfs
provides, so this cannot overflow today.

Build tested ARCH=x86_64 with GCC 16.2.0, built out of tree with
make M=tools/testing/nvdimm:
tools/testing/nvdimm/test/ndtest.o

Assisted-by: LLM
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Kees Cook <kees@kernel.org>
---
 tools/testing/nvdimm/test/ndtest.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/testing/nvdimm/test/ndtest.c b/tools/testing/nvdimm/test/ndtest.c
index 2051ad5d4882..f097f2992966 100644
--- a/tools/testing/nvdimm/test/ndtest.c
+++ b/tools/testing/nvdimm/test/ndtest.c
@@ -693,7 +693,7 @@ static ssize_t flags_show(struct device *dev,
 	if (seq_buf_used(&s))
 		seq_buf_printf(&s, "\n");
 
-	return seq_buf_used(&s);
+	return seq_buf_strlen(&s);
 }
 static DEVICE_ATTR_RO(flags);
 
-- 
2.55.0


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

* [PATCH v4 11/11] docs: core-api: Document the seq_buf API
  2026-10-03  3:59 [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Kees Cook
                   ` (9 preceding siblings ...)
  2026-10-03  3:59 ` [PATCH v4 10/11] nvdimm: ndtest: Return the string length from flags_show() Kees Cook
@ 2026-10-03  3:59 ` Kees Cook
  2026-10-03  6:32 ` [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Alexei Starovoitov
  11 siblings, 0 replies; 19+ messages in thread
From: Kees Cook @ 2026-10-03  3:59 UTC (permalink / raw)
  To: Bill Wendling
  Cc: Kees Cook, Jonathan Corbet, linux-doc, Matthew Wilcox (Oracle),
	Andrew Morton, Andy Shevchenko, Petr Mladek, Randy Dunlap,
	Shuah Khan, Steven Rostedt, David Gow, Sergey Senozhatsky,
	Shuvam Pandey, Günther Noack, Mickaël Salaün,
	Masami Hiramatsu, Mathieu Desnoyers, Jiri Kosina,
	Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
	Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
	Ihor Solodrai, Christophe Leroy (CS GROUP),
	Uwe Kleine-König, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Shivaprasad G Bhat, Thorsten Blum,
	Alison Schofield, Dave Jiang, Greg Kroah-Hartman, Guangshuo Li,
	Ira Weiny, Uwe Kleine-König, Vishal Verma, linux-kernel,
	bpf, linux-security-module, linux-trace-kernel, linuxppc-dev,
	nvdimm, linux-hardening

The kernel-doc in include/linux/seq_buf.h and lib/seq_buf.c documents
the seq_buf interface, but no .rst file pulls either of them in, so none
of it reaches the generated documentation.

Add the missing kernel-doc for seq_buf_clear() and seq_buf_init(), and a
Sequence Buffers section to the kernel API documentation. The static
internal helper seq_buf_can_fit() is left out. Additionally fix
seq_buf_hex_dump() indentation to avoid the reported Sphinx error:

  ERROR: Unexpected indentation.
  WARNING: Block quote ends without a blank line; unexpected unindent.

Verified with "make SPHINXDIRS=core-api htmldocs", which rendered
happily into core-api/kernel-api.html.

Assisted-by: LLM
Co-developed-by: Bill Wendling <morbo@google.com>
Signed-off-by: Bill Wendling <morbo@google.com>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Kees Cook <kees@kernel.org>
---
 Documentation/core-api/kernel-api.rst |  9 +++++++++
 include/linux/seq_buf.h               | 12 ++++++++++++
 lib/seq_buf.c                         | 13 +++++++------
 3 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/Documentation/core-api/kernel-api.rst b/Documentation/core-api/kernel-api.rst
index 4c4a57c1c094..f5a0aedbbb48 100644
--- a/Documentation/core-api/kernel-api.rst
+++ b/Documentation/core-api/kernel-api.rst
@@ -96,6 +96,15 @@ Error Pointers
 .. kernel-doc:: include/linux/err.h
    :internal:
 
+Sequence Buffers
+----------------
+
+.. kernel-doc:: include/linux/seq_buf.h
+   :internal:
+
+.. kernel-doc:: lib/seq_buf.c
+   :no-identifiers: seq_buf_can_fit
+
 Sorting
 -------
 
diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
index 50b1e78eeea6..c97dd9b0ba53 100644
--- a/include/linux/seq_buf.h
+++ b/include/linux/seq_buf.h
@@ -31,6 +31,10 @@ struct seq_buf {
 		.size = SIZE,				\
 	}
 
+/**
+ * seq_buf_clear - reset the seq_buf to be read / appended from the beginning
+ * @s: the seq_buf handle
+ */
 static inline void seq_buf_clear(struct seq_buf *s)
 {
 	s->len = 0;
@@ -38,6 +42,14 @@ static inline void seq_buf_clear(struct seq_buf *s)
 		s->buffer[0] = '\0';
 }
 
+/**
+ * seq_buf_init - initialize a seq_buf
+ * @s: the seq_buf handle
+ * @buf: pointer to the buffer
+ * @size: total size of @buf
+ *
+ * The contents of the buffer are ignored.
+ */
 static inline void
 seq_buf_init(struct seq_buf *s, char *buf, unsigned int size)
 {
diff --git a/lib/seq_buf.c b/lib/seq_buf.c
index 8da2e9447adf..54b76044e4ba 100644
--- a/lib/seq_buf.c
+++ b/lib/seq_buf.c
@@ -409,12 +409,13 @@ int seq_buf_to_user(struct seq_buf *s, char __user *ubuf, size_t start, int cnt)
  *
  * Function is an analogue of print_hex_dump() and thus has similar interface.
  *
- * linebuf size is maximal length for one line.
- * 32 * 3 - maximum bytes per line, each printed into 2 chars + 1 for
- *	separating space
- * 2 - spaces separating hex dump and ASCII representation
- * 32 - ASCII representation
- * 1 - terminating '\0'
+ * linebuf size is maximal length for one line::
+ *
+ *	32 * 3 - maximum bytes per line, each printed into 2 chars + 1 for
+ *		 separating space
+ *	2 - spaces separating hex dump and ASCII representation
+ *	32 - ASCII representation
+ *	1 - terminating '\0'
  *
  * Returns: zero on success, -1 on overflow.
  */
-- 
2.55.0


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

* Re: [PATCH v4 08/11] seq_buf: Add seq_buf_init_append()
  2026-10-03  3:59 ` [PATCH v4 08/11] seq_buf: Add seq_buf_init_append() Kees Cook
@ 2026-10-03  4:33   ` bot+bpf-ci
  2026-10-03 10:31     ` Kees Cook
  0 siblings, 1 reply; 19+ messages in thread
From: bot+bpf-ci @ 2026-10-03  4:33 UTC (permalink / raw)
  To: kees, morbo
  Cc: kees, willy, akpm, andriy.shevchenko, david, pmladek,
	shuvampandey1, rostedt, corbet, senozhatsky, gnoack, mic,
	mhiramat, mathieu.desnoyers, jikos, ast, daniel, andrii, eddyz87,
	memxor, martin.lau, song, yonghong.song, jolsa, emil,
	ihor.solodrai, chleroy, u.kleine-koenig, maddy, mpe, npiggin,
	sbhat, blum, alison.schofield, dave.jiang, gregkh,
	lgs201920130244, iweiny, u.kleine-koenig, vishal.l.verma,
	rdunlap, skhan, linux-kernel, bpf, linux-security-module,
	linux-trace-k

[-- Attachment #1: Type: text/plain, Size: 2049 bytes --]

> seq_buf: Add seq_buf_init_append()

TL;DR: Commit message only: it says seq_buf_init() sets the buffer size to 0
via seq_buf_clear(), but it resets the length, not the size. The code is not
reported as buggy.

> seq_buf: Add seq_buf_init_append()
>
> Several strlcat() call sites being converted to seq_buf need behavior
> seq_buf doesn't currently provide. The normal seq_buf_init() always
> sets the new buffer size to 0 via seq_buf_clear(). Code migrating from
> strlcat(buf, ...), which appends to whatever buf already contains,
> can't use seq_buf_init() without discarding that existing content. Add
> seq_buf_init_append(), which preserves the existing contents and positions
> the seq_buf to append after it. A buffer with no NUL within its size
> starts out overflowed, as strlcat() treats it as already truncated.
> Add KUnit tests for behavior coverage.
>
> Tests passed under qemu on ARCH=x86_64 with GCC 16.2.0 and CONFIG_KASAN=y,
> and on big-endian ARCH=s390 with GCC s390x-linux-gnu 16.2.0.
>
> Assisted-by: LLM
> Signed-off-by: Bill Wendling <morbo@google.com>
> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Co-developed-by: Kees Cook <kees@kernel.org>
> Signed-off-by: Kees Cook <kees@kernel.org>

This isn't a bug in the code, but is the changelog accurate when it says
seq_buf_init() "always sets the new buffer size to 0 via seq_buf_clear()"?

seq_buf_init() stores the size it was given in s->size, and seq_buf_clear()
leaves s->size alone.  It only resets the length and terminates the first
byte:

    static inline void seq_buf_clear(struct seq_buf *s)
    {
    	s->len = 0;
    	if (s->size)
    		s->buffer[0] = '\0';
    }

Should the sentence say that seq_buf_init() always sets the length (s->len)
to 0 via seq_buf_clear(), rather than the buffer size?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/37096036516

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

* Re: [PATCH v4 01/11] seq_buf: Do not print an empty line from an overflowed seq_buf_do_printk()
  2026-10-03  3:59 ` [PATCH v4 01/11] seq_buf: Do not print an empty line from an overflowed seq_buf_do_printk() Kees Cook
@ 2026-10-03  4:50   ` bot+bpf-ci
  0 siblings, 0 replies; 19+ messages in thread
From: bot+bpf-ci @ 2026-10-03  4:50 UTC (permalink / raw)
  To: kees, morbo
  Cc: kees, akpm, david, pmladek, senozhatsky, shuvampandey1, rostedt,
	corbet, gnoack, willy, mic, andriy.shevchenko, mhiramat,
	mathieu.desnoyers, jikos, ast, daniel, andrii, eddyz87, memxor,
	martin.lau, song, yonghong.song, jolsa, emil, ihor.solodrai,
	chleroy, u.kleine-koenig, maddy, mpe, npiggin, sbhat, blum,
	alison.schofield, dave.jiang, gregkh, lgs201920130244, iweiny,
	u.kleine-koenig, vishal.l.verma, rdunlap, skhan, linux-kernel,
	bpf, linux-security-module, linux-trace-k

[-- Attachment #1: Type: text/plain, Size: 3003 bytes --]

> seq_buf: Do not print an empty line from an overflowed seq_buf_do_printk()

TL;DR: The new KUnit test may fail spuriously, and stop detecting the bug,
when console_loglevel suppresses KERN_INFO (quiet, loglevel=N) or
CONFIG_PRINTK=n, since the capture console never sees records.

> diff --git a/lib/tests/seq_buf_kunit.c b/lib/tests/seq_buf_kunit.c
> index eb466386bbef..0934dfb602ff 100644
> --- a/lib/tests/seq_buf_kunit.c
> +++ b/lib/tests/seq_buf_kunit.c

[ ... ]

> +static void seq_buf_printk_run(struct console *capture, struct seq_buf *s)
> +{
> +	seq_buf_printk_marked = 0;
> +	seq_buf_printk_empty = 0;
> +	seq_buf_printk_last_was_ours = false;
> +
> +	/*
> +	 * register_console() will not take an unmatched console without
> +	 * CON_ENABLED, and unregister_console() clears it, so set it on
> +	 * every run to keep the test repeatable.
> +	 */
> +	capture->flags = CON_ENABLED;
> +	register_console(capture);
> +	seq_buf_do_printk(s, KERN_INFO);
> +	unregister_console(capture);
> +}

Does this test depend on console_loglevel being above LOGLEVEL_INFO?

The records are printed at KERN_INFO, and a legacy console only gets a record
through console_emit_next_record(), which calls printk_get_next_message()
with may_suppress set.  That function skips any record for which
suppress_message_printing() is true:

    static bool suppress_message_printing(int level)
    {
            return (level >= console_loglevel && !ignore_loglevel);
    }

A skipped record just advances con->seq and ->write() is never called.

With console_loglevel <= 6, seq_buf_printk_capture() never runs and
seq_buf_printk_marked stays 0, so these expectations in
seq_buf_do_printk_test() fail:

    KUNIT_EXPECT_EQ(test, seq_buf_printk_marked, 1);
    KUNIT_EXPECT_EQ(test, seq_buf_printk_marked, 2);
    KUNIT_EXPECT_EQ(test, seq_buf_printk_marked, 1);

The seq_buf_printk_empty checks would still pass in that setup, so the test
would also stop detecting the bug it was written for.

CONFIG_CONSOLE_LOGLEVEL_QUIET is 4 when "quiet" is on the command line.  A
loglevel=N boot argument, dmesg -n, or a kernel.printk sysctl that lowers the
console level has the same effect, and some distributions lower it by
default.  With CONFIG_SEQ_BUF_KUNIT_TEST=m this would report a failure.  The
same happens with CONFIG_PRINTK=n, where printk() is a no-op and
SEQ_BUF_KUNIT_TEST depends only on KUNIT.

The kunit.py qemu and UML runs would not notice, since they do not lower the
loglevel.  The later commits in this series still call
seq_buf_do_printk(s, KERN_INFO) unchanged.

Would it make sense to use a level that is not filtered, call kunit_skip()
when console_loglevel would suppress KERN_INFO, or read the records back from
the printk ring buffer instead of a console?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/37096036516

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

* Re: [PATCH v4 04/11] seq_buf: Clear what a writer did not claim when a seq_buf overflows
  2026-10-03  3:59 ` [PATCH v4 04/11] seq_buf: Clear what a writer did not claim when a seq_buf overflows Kees Cook
@ 2026-10-03  4:50   ` bot+bpf-ci
  0 siblings, 0 replies; 19+ messages in thread
From: bot+bpf-ci @ 2026-10-03  4:50 UTC (permalink / raw)
  To: kees, morbo
  Cc: kees, willy, akpm, andriy.shevchenko, david, jikos, pmladek,
	shuvampandey1, rostedt, corbet, senozhatsky, gnoack, mic,
	mhiramat, mathieu.desnoyers, ast, daniel, andrii, eddyz87,
	memxor, martin.lau, song, yonghong.song, jolsa, emil,
	ihor.solodrai, chleroy, u.kleine-koenig, maddy, mpe, npiggin,
	sbhat, blum, alison.schofield, dave.jiang, gregkh,
	lgs201920130244, iweiny, u.kleine-koenig, vishal.l.verma,
	rdunlap, skhan, linux-kernel, bpf, linux-security-module,
	linux-trace-k

[-- Attachment #1: Type: text/plain, Size: 2929 bytes --]

> seq_buf: Clear what a writer did not claim when a seq_buf overflows

TL;DR: Commit message only: the claim that seq_buf_path() was "never exported,
unlike the other writers" looks inaccurate, since several other writers are
also unexported. The code and export look correct.

>     seq_buf: Clear what a writer did not claim when a seq_buf overflows
>
>     Using seq_buf_set_overflow() would leave the bytes between "len"
>     and "size" untouched, so if seq_buf_str() is used on an overflowed
>     seq_buf, those bytes may be exposed. For any paths that don't claim
>     partially written bytes, by setting "len = size" before calling
>     seq_buf_set_overflow(), wipe the unclaimed bytes. The seq_buf_puts()
>     and related APIs already claim those bytes now, so only the unclaimed
>     cases remain. A specific example of this was seq_buf_path() which uses
>     d_path() and would write to the tail before discovering it was out
>     of space, and would correctly mark a seq_buf as overflowed, but the
>     path fragment would be left over.
>
>     Clear from len to the end of the buffer in seq_buf_set_overflow(), which
>     every overflow goes through, including seq_buf_commit() with a negative
>     count.
>
>     Add a test that fills a seq_buf, leaves it too little room for a path, and
>     checks that nothing of the path is left in the buffer. The tests run before
>     anything writable is mounted, so it takes its file from shmem.
>
>     seq_buf_path() was never exported, unlike the other writers, so the
>     test failed to link as a module. Export it.
>
>     Tests passed under qemu on ARCH=x86_64 with GCC 16.2.0 and CONFIG_KASAN=y,
>     and on big-endian ARCH=s390 with GCC s390x-linux-gnu 16.2.0, and the
>     test builds as a module (CONFIG_SEQ_BUF_KUNIT_TEST=m).
>
>     Assisted-by: LLM
>     Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
>     Signed-off-by: Kees Cook <kees@kernel.org>

This isn't a bug, but is the comparison in "seq_buf_path() was never
exported, unlike the other writers" accurate?

In lib/seq_buf.c the only writers with an export are seq_buf_printf(),
seq_buf_puts(), seq_buf_putc(), seq_buf_putmem() and seq_buf_putmem_hex()
(plus seq_buf_do_printk(), which is not a writer).  seq_buf_vprintf(),
seq_buf_bprintf() and seq_buf_hex_dump() also write into a seq_buf and have
no EXPORT_SYMBOL_GPL().

The reason for the export, that the new KUnit test calls seq_buf_path() and
has to link with CONFIG_SEQ_BUF_KUNIT_TEST=m, looks correct.  Could the
sentence drop the "unlike the other writers" part, for example:

  seq_buf_path() was not exported, so the test failed to link as a module.
  Export it.


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/37096036516

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

* Re: [PATCH v4 03/11] seq_buf: Copy what fits when seq_buf_puts() and seq_buf_putmem() overflow
  2026-10-03  3:59 ` [PATCH v4 03/11] seq_buf: Copy what fits when seq_buf_puts() and seq_buf_putmem() overflow Kees Cook
@ 2026-10-03  4:50   ` bot+bpf-ci
  0 siblings, 0 replies; 19+ messages in thread
From: bot+bpf-ci @ 2026-10-03  4:50 UTC (permalink / raw)
  To: kees, morbo
  Cc: kees, willy, akpm, andriy.shevchenko, david, pmladek,
	shuvampandey1, rostedt, corbet, senozhatsky, gnoack, mic,
	mhiramat, mathieu.desnoyers, jikos, ast, daniel, andrii, eddyz87,
	memxor, martin.lau, song, yonghong.song, jolsa, emil,
	ihor.solodrai, chleroy, u.kleine-koenig, maddy, mpe, npiggin,
	sbhat, blum, alison.schofield, dave.jiang, gregkh,
	lgs201920130244, iweiny, u.kleine-koenig, vishal.l.verma,
	rdunlap, skhan, linux-kernel, bpf, linux-security-module,
	linux-trace-k

[-- Attachment #1: Type: text/plain, Size: 3117 bytes --]

> seq_buf: Copy what fits when seq_buf_puts() and seq_buf_putmem() overflow

TL;DR: seq_buf_puts() now copies a partial string on overflow, which may break
lpfc_rx_monitor_report(), which relies on whole-record drops. Debugfs output
could then end in a truncated record followed by a duplicate.

> diff --git a/lib/seq_buf.c b/lib/seq_buf.c
> index 35a5964370b4..60e9eadb3ef7 100644
> --- a/lib/seq_buf.c
> +++ b/lib/seq_buf.c

[ ... ]

> @@ -194,6 +196,11 @@ int seq_buf_puts(struct seq_buf *s, const char *str)
>  		s->len += len - 1;
>  		return 0;
>  	}
> +	/* Copy what fits, so the buffer never holds stale bytes */
> +	if (s->len < s->size) {
> +		strscpy(s->buffer + s->len, str, s->size - s->len);
> +		s->len = s->size;
> +	}
>  	seq_buf_set_overflow(s);
>  	return -1;
>  }

Can this break callers that rely on seq_buf_puts() dropping a string whole
when it does not fit?

lpfc_rx_monitor_report() in drivers/scsi/lpfc/lpfc_sli.c was converted to
seq_buf in 22d4cbf6f750 ("scsi: lpfc: Replace strlcat() with seq_buf in
lpfc_rx_monitor_report()"), and that changelog says seq_buf_puts() "appends
it only when it fits whole, so the output keeps ending at the last complete
record".  The code still documents this:

	/*
	 * Drop a record whole if it does not fit, without
	 * consuming its ring entry.
	 */
	scnprintf(tmp, sizeof(tmp), "%03d:\t%-16llu...", ...);

	if (seq_buf_puts(&s, tmp) < 0)
		break;

With this patch, a record that does not fit is now copied up to
buf[buf_len - 2] and NUL-terminated at buf[buf_len - 1] before the loop
breaks.

lpfc_rx_monitor_read() then returns strlen(buffer) bytes through
simple_read_from_buffer(), so the debugfs output ends in a truncated record
with no newline.

Since the loop breaks before *head_idx is advanced, the same ring entry is
printed again in full at the start of the next read, so the reader sees a
partial copy followed by a duplicate.

Both the old strlcat() code ("if ((strlen(buf) + strlen(tmp)) >= buf_len)
break;") and the seq_buf version before this patch dropped the record whole.

Getting there needs the 800 rows to average more than about 128 bytes in the
128 * LPFC_MAX_RXMONITOR_ENTRY buffer, which can happen when the u64/u32
counters (cmf_info, max_read_cnt, avg_io_latency, timer_utilization, ...)
are wider than their %-8/%-16 columns.

The commit message does not mention auditing seq_buf_puts() callers that
rely on the old semantics, and nothing later in the series changes lpfc.

Would it make sense to have lpfc_rx_monitor_report() check
strlen(tmp) < seq_buf_buffer_left(&s) before calling seq_buf_puts()?

The other callers I checked (setup_trace_event(), the usbhid name building,
the partition pp_buf users, string_stream_get_string(), dynevent_str_add()
and the hist command builders) either treat overflow as an error or used
strlcat() before, which also copied a partial string.


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/37096036516

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

* Re: [PATCH v4 00/11] seq_buf: Add seq_buf_strlen()
  2026-10-03  3:59 [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Kees Cook
                   ` (10 preceding siblings ...)
  2026-10-03  3:59 ` [PATCH v4 11/11] docs: core-api: Document the seq_buf API Kees Cook
@ 2026-10-03  6:32 ` Alexei Starovoitov
  11 siblings, 0 replies; 19+ messages in thread
From: Alexei Starovoitov @ 2026-10-03  6:32 UTC (permalink / raw)
  To: Kees Cook, Bill Wendling
  Cc: Jonathan Corbet, Andrew Morton, David Gow, Petr Mladek,
	Sergey Senozhatsky, Shuvam Pandey, Steven Rostedt,
	Günther Noack, Matthew Wilcox (Oracle),
	Mickaël Salaün, Andy Shevchenko, Masami Hiramatsu,
	Mathieu Desnoyers, Jiri Kosina, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
	Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
	Ihor Solodrai, Christophe Leroy (CS GROUP),
	Uwe Kleine-König, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Shivaprasad G Bhat, Thorsten Blum,
	Alison Schofield, Dave Jiang, Greg Kroah-Hartman, Guangshuo Li,
	Ira Weiny, Uwe Kleine-König, Vishal Verma, Randy Dunlap,
	Shuah Khan, linux-kernel, bpf, linux-security-module,
	linux-trace-kernel, linuxppc-dev, nvdimm, linux-doc,
	linux-hardening

On Fri, Oct 02, 2026 at 08:59 PM Kees Cook <kees@kernel.org> wrote:
>   seq_buf: Add seq_buf_terminate()
>   bpf: Remove dead newline stripping from format_disasm_line()

For bpf bits:
Acked-by: Alexei Starovoitov <ast@kernel.org>

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

* Re: [PATCH v4 08/11] seq_buf: Add seq_buf_init_append()
  2026-10-03  4:33   ` bot+bpf-ci
@ 2026-10-03 10:31     ` Kees Cook
  0 siblings, 0 replies; 19+ messages in thread
From: Kees Cook @ 2026-10-03 10:31 UTC (permalink / raw)
  To: bot+bpf-ci
  Cc: morbo, willy, akpm, andriy.shevchenko, david, pmladek,
	shuvampandey1, rostedt, corbet, senozhatsky, gnoack, mic,
	mhiramat, mathieu.desnoyers, jikos, ast, daniel, andrii, eddyz87,
	memxor, martin.lau, song, yonghong.song, jolsa, emil,
	ihor.solodrai, chleroy, u.kleine-koenig, maddy, mpe, npiggin,
	sbhat, blum, alison.schofield, dave.jiang, gregkh,
	lgs201920130244, iweiny, u.kleine-koenig, vishal.l.verma,
	rdunlap, skhan, linux-kernel, bpf, linux-security-module,
	linux-trace-k, ernel, linuxppc-dev, nvdimm, linux-doc,
	linux-hardening, martin.lau, mason

On Sat, Oct 03, 2026 at 04:33:59AM +0000, bot+bpf-ci@kernel.org wrote:
> This isn't a bug in the code, but is the changelog accurate when it says
> seq_buf_init() "always sets the new buffer size to 0 via seq_buf_clear()"?

Yeah, fair. It's not worded clearly. I've fixed the wording for v5
to say seq_buf_init() 'empties the buffer (sets its "len" to 0) via
seq_buf_clear()'.

-Kees

-- 
Kees Cook

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

* Re: [PATCH v4 05/11] seq_buf: Add seq_buf_strlen()
  2026-10-03  3:59 ` [PATCH v4 05/11] seq_buf: Add seq_buf_strlen() Kees Cook
@ 2026-10-03 15:36   ` Andy Shevchenko
  0 siblings, 0 replies; 19+ messages in thread
From: Andy Shevchenko @ 2026-10-03 15:36 UTC (permalink / raw)
  To: Kees Cook
  Cc: Bill Wendling, Matthew Wilcox (Oracle),
	Andrew Morton, David Gow, Petr Mladek, Shuvam Pandey,
	Steven Rostedt, Jonathan Corbet, Sergey Senozhatsky,
	Günther Noack, Mickaël Salaün, Masami Hiramatsu,
	Mathieu Desnoyers, Jiri Kosina, Alexei Starovoitov,
	Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jiri Olsa, Emil Tsalapatis, Ihor Solodrai,
	Christophe Leroy (CS GROUP),
	Uwe Kleine-König, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Shivaprasad G Bhat, Thorsten Blum,
	Alison Schofield, Dave Jiang, Greg Kroah-Hartman, Guangshuo Li,
	Ira Weiny, Uwe Kleine-König, Vishal Verma, Randy Dunlap,
	Shuah Khan, linux-kernel, bpf, linux-security-module,
	linux-trace-kernel, linuxppc-dev, nvdimm, linux-doc,
	linux-hardening

On Fri, Oct 02, 2026 at 08:59:10PM -0700, Kees Cook wrote:
> Several strlcat() call sites being converted to seq_buf need behavior
> seq_buf doesn't currently provide. The return from seq_buf_used() is
> not the length of the string in a seq_buf. Once the buffer is full or
> has overflowed it returns the buffer size, which counts the byte that
> seq_buf_str() replaces with the NUL, so a caller that needs the string
> and its length has to call seq_buf_str() and then walk the string with
> strlen().
> 
> Move the termination out of seq_buf_str() into a helper that returns
> where it put the NUL, and add seq_buf_strlen(), which terminates the
> buffer in the same way and returns that offset.
> 
> As discussed in review, don't add WARN_ON() for seq_buf_strlen() and
> drop it from seq_buf_str().
> 
> Add tests comparing seq_buf_strlen() against strlen() of seq_buf_str()
> for empty, appended, truncated, exactly full, and overflowed buffers,
> checking that seq_buf_strlen() alone terminates a full buffer, and
> checking that a zero-sized seq_buf reports an empty string from both
> accessors without touching the buffer.
> 
> Tests passed under qemu on ARCH=x86_64 with GCC 16.2.0 and CONFIG_KASAN=y,
> and on big-endian ARCH=s390 with GCC s390x-linux-gnu 16.2.0.

...

>  static inline const char *seq_buf_str(struct seq_buf *s)
>  {
> -	if (WARN_ON(s->size == 0))
> +	if (s->size == 0)
>  		return "";
>  
> -	if (seq_buf_buffer_left(s))
> -		s->buffer[s->len] = 0;
> -	else
> -		s->buffer[s->size - 1] = 0;
> +	__seq_buf_terminate(s);
>  
>  	return s->buffer;
>  }

Looking at this again, can't it be rewritten now using _strlen()?

	if (seq_buf_strlen(s))
		return s->buffer;

	return "";

?

...

> +static inline size_t seq_buf_strlen(struct seq_buf *s)
> +{
> +	if (s->size == 0)
> +		return 0;
> +
> +	return __seq_buf_terminate(s);
> +}

(Left for the context to the above.)

-- 
With Best Regards,
Andy Shevchenko



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

end of thread, other threads:[~2026-10-03 15:37 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-03  3:59 [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Kees Cook
2026-10-03  3:59 ` [PATCH v4 01/11] seq_buf: Do not print an empty line from an overflowed seq_buf_do_printk() Kees Cook
2026-10-03  4:50   ` bot+bpf-ci
2026-10-03  3:59 ` [PATCH v4 02/11] seq_buf: Do not pop from an overflowed seq_buf Kees Cook
2026-10-03  3:59 ` [PATCH v4 03/11] seq_buf: Copy what fits when seq_buf_puts() and seq_buf_putmem() overflow Kees Cook
2026-10-03  4:50   ` bot+bpf-ci
2026-10-03  3:59 ` [PATCH v4 04/11] seq_buf: Clear what a writer did not claim when a seq_buf overflows Kees Cook
2026-10-03  4:50   ` bot+bpf-ci
2026-10-03  3:59 ` [PATCH v4 05/11] seq_buf: Add seq_buf_strlen() Kees Cook
2026-10-03 15:36   ` Andy Shevchenko
2026-10-03  3:59 ` [PATCH v4 06/11] seq_buf: Add seq_buf_terminate() Kees Cook
2026-10-03  3:59 ` [PATCH v4 07/11] bpf: Remove dead newline stripping from format_disasm_line() Kees Cook
2026-10-03  3:59 ` [PATCH v4 08/11] seq_buf: Add seq_buf_init_append() Kees Cook
2026-10-03  4:33   ` bot+bpf-ci
2026-10-03 10:31     ` Kees Cook
2026-10-03  3:59 ` [PATCH v4 09/11] powerpc/papr_scm: Return the string length from the sysfs show functions Kees Cook
2026-10-03  3:59 ` [PATCH v4 10/11] nvdimm: ndtest: Return the string length from flags_show() Kees Cook
2026-10-03  3:59 ` [PATCH v4 11/11] docs: core-api: Document the seq_buf API Kees Cook
2026-10-03  6:32 ` [PATCH v4 00/11] seq_buf: Add seq_buf_strlen() Alexei Starovoitov

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

all inboxes | Powered by JetHome®