seq_buf: Introduce DECLARE_SEQ_BUF and seq_buf_str()
Subsystems (2)
PATCH v2 1/1 seq_buf: Introduce DECLARE_SEQ_BUF and seq_buf_str()
Solve two ergonomic issues with struct seq_buf;
1) Too much boilerplate is required to initialize:
struct seq_buf s; char buf[32]; seq_buf_init(s, buf, sizeof(buf));
Instead, we can build this directly on the stack. Provide DECLARE_SEQ_BUF() macro to do this:
DECLARE_SEQ_BUF(s, 32);
2) %NUL termination is fragile and requires 2 steps to get a valid C String (and is a layering violation exposing the "internals" of seq_buf):
seq_buf_terminate(s); do_something(s->buffer);
Instead, we can just return s->buffer direction after terminating it in refactored seq_buf_terminate(), now known as seq_buf_str():
do_soemthing(seq_buf_str(s));
---
v2 - rename seq_buf_cstr() to seq_buf_str() (rostedt)
v1 - https://lore.kernel.org/all/[email protected]/
--- include/linux/seq_buf.h | 19 +++++++++++++++---- kernel/trace/trace.c | 11 +---------- lib/seq_buf.c | 4 +--- 3 files changed, 17 insertions(+), 17 deletions(-)
include/linux/seq_buf.h +15 -4
@@ -21,9 +21,16 @@ struct seq_buf {size_t len; }; +#define DECLARE_SEQ_BUF (NAME ,SIZE ) \ + char __ ## NAME ## _buffer[SIZE] = ""; \ + struct seq_buf NAME = { .buffer = &__ ## NAME ## _buffer, \ + .size = SIZE } +static inline void seq_buf_clear (struct seq_buf *s) { s->len =0 ; +if (s->size) + s->buffer[0 ] ='\0' ; } static inline void
@@ -69,8 +76,8 @@ static inline unsigned int seq_buf_used(struct seq_buf *s) }/** -* seq_buf_terminate - Make sure buffer is nul terminated -* @s: the seq_buf descriptor to terminate. +* seq_buf_str - get %NUL-terminated C string from seq_buf +* @s: the seq_buf handle * * This makes sure that the buffer in @s is nul terminated and * safe to read as a string.
@@ -81,16 +88,20 @@ static inline unsigned int seq_buf_used(struct seq_buf *s)* * After this function is called, s->buffer is safe to use * in string operations. +* +* Returns @s->buf after making sure it is terminated. */ -static inline void seq_buf_terminate (struct seq_buf *s) +static inline char *seq_buf_str (struct seq_buf *s) {if (WARN_ON (s->size ==0 )) -return ; +return "" ;if (seq_buf_buffer_left (s)) s->buffer[s->len] =0 ;else s->buffer[s->size -1 ] =0 ; + +return s->buffer; }/**
kernel/trace/trace.c +1 -10
@@ -3828,15 +3828,6 @@ static bool trace_safe_str(struct trace_iterator *iter, const char *str,return false; } -static const char *show_buffer (struct trace_seq *s) -{ -struct seq_buf *seq = &s->seq; - -seq_buf_terminate (seq); - -return seq->buffer; -} -static DEFINE_STATIC_KEY_FALSE (trace_no_verify);static int test_can_verify_check (const char *fmt, ...)
@@ -3976,7 +3967,7 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,*/ if (WARN_ONCE (!trace_safe_str (iter, str, star, len),"fmt: '%s' current_buffer: '%s'" , - fmt,show_buffer (&iter->seq))) { + fmt,seq_buf_str (&iter->seq.seq))) {int ret;/* Try to safely read the string */
lib/seq_buf.c +1 -3
@@ -109,9 +109,7 @@ void seq_buf_do_printk(struct seq_buf *s, const char *lvl)if (s->size ==0 || s->len ==0 )return ; -seq_buf_terminate (s); - - start = s->buffer; + start =seq_buf_str (s);while ((lf =strchr (start,'\n' ))) {int len = lf - start +1 ;
Signature · 1 line
2.34.1
Steven Rostedt
maintainerinclude/linux/seq_buf.h static inline unsigned int seq_buf_used(struct seq_buf *s) · line 94
Quoted 9 lines · On Thu, 26 Oct 2023 12:40:37 -0700 Kees Cook <[email protected]> wrote:
include/linux/seq_buf.h +3
-1@@ -81,16 +88,20 @@ static inline unsigned int seq_buf_used(struct seq_buf *s)* * After this function is called, s->buffer is safe to use * in string operations. +* +* Returns @s->buf after making sure it is terminated. */ -static inline void seq_buf_terminate (struct seq_buf *s) +static inline char *seq_buf_str (struct seq_buf *s)
Looking at show_buffer() (below), I wonder if this should be:
static inline const char *seq_buf_str() ?
I mean, it can be modified, but do we want to allow that?
-- Steve
Quoted 33 lines
include/linux/seq_buf.h +3
-1{if (WARN_ON (s->size ==0 )) -return ; +return "" ;if (seq_buf_buffer_left (s)) s->buffer[s->len] =0 ;else s->buffer[s->size -1 ] =0 ; + +return s->buffer; }/** kernel/trace/trace.c +0
-9@@ -3828,15 +3828,6 @@ static bool trace_safe_str(struct trace_iterator *iter, const char *str,return false; } -static const char *show_buffer (struct trace_seq *s) -{ -struct seq_buf *seq = &s->seq; - -seq_buf_terminate (seq); - -return seq->buffer; -} -static DEFINE_STATIC_KEY_FALSE (trace_no_verify);
Quoted 15 lines · Steven Rostedt
include/linux/seq_buf.h static inline unsigned int seq_buf_used(struct seq_buf *s) · line 94
Quoted 9 lines · On Thu, 26 Oct 2023 12:40:37 -0700 Kees Cook <[email protected]> wrote:
include/linux/seq_buf.h +3
-1@@ -81,16 +88,20 @@ static inline unsigned int seq_buf_used(struct seq_buf *s)* * After this function is called, s->buffer is safe to use * in string operations. +* +* Returns @s->buf after making sure it is terminated. */ -static inline void seq_buf_terminate (struct seq_buf *s) +static inline char *seq_buf_str (struct seq_buf *s)Looking at
show_buffer()(below), I wonder if this should be:
Quoted 3 lines
static inline const char
*seq_buf_str()?I mean, it can be modified, but do we want to allow that?
Yeah, good idea. I've updated this for v3.
Signature · 1 line
Kees Cook
Quoted 25 lines · Kees Cook
Solve two ergonomic issues with struct
seq_buf;1) Too much boilerplate is required to initialize:
struct seq_buf s; char buf[32]; seq_buf_init(s, buf, sizeof(buf));Instead, we can build this directly on the stack. Provide
DECLARE_SEQ_BUF()macro to do this:DECLARE_SEQ_BUF(s, 32);2) %NUL termination is fragile and requires 2 steps to get a valid C String (and is a layering violation exposing the "internals" of
seq_buf):seq_buf_terminate(s); do_something(s->buffer);Instead, we can just return
s->bufferdirection after terminating it in refactoredseq_buf_terminate(), now known asseq_buf_str():do_soemthing(seq_buf_str(s));
...
include/linux/seq_buf.h struct seq_buf { · line 27
Quoted 4 lines
include/linux/seq_buf.h +4
-0+#define DECLARE_SEQ_BUF (NAME ,SIZE ) \ + char __ ## NAME ## _buffer[SIZE] = ""; \ + struct seq_buf NAME = { .buffer = &__ ## NAME ## _buffer, \ + .size = SIZE }
Hmm... Wouldn't be more readable to have it as
#define DECLARE_SEQ_BUF(NAME, SIZE) \
char __ ## NAME ## _buffer[SIZE] = ""; \
struct seq_buf NAME = { \
.buffer = &__ ## NAME ## _buffer, \
.size = SIZE, \
}?
...
include/linux/seq_buf.h static inline unsigned int seq_buf_used(struct seq_buf *s) · line 97
Quoted 5 lines
include/linux/seq_buf.h +2
-1+static inline char *seq_buf_str (struct seq_buf *s) {if (WARN_ON (s->size ==0 )) -return ; +return "" ;
I'm wondering why it's a problem to have an empty string?
Quoted 7 lines
include/linux/seq_buf.h +2
-0if (seq_buf_buffer_left (s)) s->buffer[s->len] =0 ;else s->buffer[s->size -1 ] =0 ; + +return s->buffer; }
Signature · 2 lines
With Best Regards, Andy Shevchenko
Steven Rostedt
maintainerQuoted 7 lines · On Thu, 26 Oct 2023 23:20:15 +0300 Andy Shevchenko…
include/linux/seq_buf.h struct seq_buf { · line 27
Quoted 4 lines
include/linux/seq_buf.h +4
-0+#define DECLARE_SEQ_BUF (NAME ,SIZE ) \ + char __ ## NAME ## _buffer[SIZE] = ""; \ + struct seq_buf NAME = { .buffer = &__ ## NAME ## _buffer, \ + .size = SIZE }Hmm... Wouldn't be more readable to have it as
Quoted 8 lines
#define DECLARE_SEQ_BUF(NAME, SIZE) \ char __ ## NAME ## _buffer[SIZE] = ""; \ struct seq_buf NAME = { \ .buffer = &__ ## NAME ## _buffer, \ .size = SIZE, \ }?
I agree with the above.
Quoted 10 lines
...
include/linux/seq_buf.h static inline unsigned int seq_buf_used(struct seq_buf *s) · line 97
Quoted 5 lines
include/linux/seq_buf.h +2
-1+static inline char *seq_buf_str (struct seq_buf *s) {if (WARN_ON (s->size ==0 )) -return ; +return "" ;I'm wondering why it's a problem to have an empty string?
Not sure what you mean? With s->size = 0, s->buffer may not have been assigned. That shouldn't be the case, but it does make it more robust.
-- Steve
Quoted 9 lines
Quoted 8 lines
include/linux/seq_buf.h +2
-0if (seq_buf_buffer_left (s)) s->buffer[s->len] =0 ;else s->buffer[s->size -1 ] =0 ; + +return s->buffer; }
Quoted 36 lines · Andy Shevchenko
Quoted 25 lines · Kees Cook
Solve two ergonomic issues with struct
seq_buf;1) Too much boilerplate is required to initialize:
struct seq_buf s; char buf[32]; seq_buf_init(s, buf, sizeof(buf));Instead, we can build this directly on the stack. Provide
DECLARE_SEQ_BUF()macro to do this:DECLARE_SEQ_BUF(s, 32);2) %NUL termination is fragile and requires 2 steps to get a valid C String (and is a layering violation exposing the "internals" of
seq_buf):seq_buf_terminate(s); do_something(s->buffer);Instead, we can just return
s->bufferdirection after terminating it in refactoredseq_buf_terminate(), now known asseq_buf_str():do_soemthing(seq_buf_str(s));...
include/linux/seq_buf.h struct seq_buf { · line 27
Quoted 4 lines
include/linux/seq_buf.h +4
-0+#define DECLARE_SEQ_BUF (NAME ,SIZE ) \ + char __ ## NAME ## _buffer[SIZE] = ""; \ + struct seq_buf NAME = { .buffer = &__ ## NAME ## _buffer, \ + .size = SIZE }Hmm... Wouldn't be more readable to have it as
Quoted 8 lines
#define DECLARE_SEQ_BUF(NAME, SIZE) \ char __ ## NAME ## _buffer[SIZE] = ""; \ struct seq_buf NAME = { \ .buffer = &__ ## NAME ## _buffer, \ .size = SIZE, \ }?
Yes, I don't know why I did it the smooshed way. Fixed for v3.
Quoted 7 lines
include/linux/seq_buf.h static inline unsigned int seq_buf_used(struct seq_buf *s) · line 97
Quoted 5 lines
include/linux/seq_buf.h +2
-1+static inline char *seq_buf_str (struct seq_buf *s) {if (WARN_ON (s->size ==0 )) -return ; +return "" ;I'm wondering why it's a problem to have an empty string?
Well, it's a pathological case where "size" is 0 -- it shouldn't happen (hence the warn), but it's more robust to return an empty .data string pointer than a NULL s->buffer or an s->buffer that isn't intended to be used (i.e. the size == 0).
Signature · 1 line
Kees Cook
Quoted 9 lines · Kees Cook
Solve two ergonomic issues with struct
seq_buf;1) Too much boilerplate is required to initialize:
struct seq_buf s; char buf[32]; seq_buf_init(s, buf, sizeof(buf));
Quoted 4 lines
Instead, we can build this directly on the stack. Provide
DECLARE_SEQ_BUF()macro to do this:DECLARE_SEQ_BUF(s, 32);
DECLARE_SEQ_BUF_ONSTACK maybe? But otherwise this looks like a good concept.
Quoted 4 lines
Instead, we can just return
s->bufferdirection after terminating it in refactoredseq_buf_terminate(), now known asseq_buf_str():do_soemthing(seq_buf_str(s));
Looks good. Btw, one typical do_something would be printing it, so adding a format specifier that's using this helper would also probably be very useful.
Quoted 8 lines · Christoph Hellwig
Quoted 4 lines
Instead, we can just return
s->bufferdirection after terminating it in refactoredseq_buf_terminate(), now known asseq_buf_str():do_soemthing(seq_buf_str(s));Looks good. Btw, one typical
do_somethingwould be printing it, so adding a format specifier that's using this helper would also probably be very useful.
my hope is to get vsprintf.c completely refactored to use seq_buf internally, and then printsb(&sb) would actually be a primitive we'd have insted of printk("%pSB", &sb);
this would btw let us get rid of the entire %pFOO infrastructure. and make dump_page() far less crap.
Quoted 10 lines · Christoph Hellwig
Quoted 9 lines · Kees Cook
Solve two ergonomic issues with struct
seq_buf;1) Too much boilerplate is required to initialize:
struct seq_buf s; char buf[32]; seq_buf_init(s, buf, sizeof(buf));
Quoted 7 lines
Quoted 4 lines
Instead, we can build this directly on the stack. Provide
DECLARE_SEQ_BUF()macro to do this:DECLARE_SEQ_BUF(s, 32);
DECLARE_SEQ_BUF_ONSTACKmaybe? But otherwise this looks like a good concept.
It's usable for globals too... also it's a shorter name as-is. :)
Signature · 1 line
Kees Cook