seq_buf: Fix seq_buf_vprintf() truncation
Linus,
While working on merging seq_file and seq_buf code, I hit a bug and came down to a bug in seq_buf_printf(). This is suppose to be a all or nothing function. That is, either all of the string passed in is saved to the seq_buf buffer, or none of it. But due to the way vsnprintf() works in the kernel, if the line is truncated, the return value is the amount written to the buffer, including the '\0'. Then the test in seq_buf_printf() uses the returned length to see if it would fit in the buffer. It will, but it will also fill the buffer. The test to see if vsnprintf() overflowed or not is to see if the length returned is equal to or greater than the length of the buffer passed to it. Not just greater than.
Please pull the latest trace-fixes-v4.0-rc2 tree, which can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git
trace-fixes-v4.0-rc2
Tag SHA1: 2edf55a81800ad8200a02de31c8099edbdbd283f Head SHA1: 4a8fe4e1811c96ad0ad9f4083f2fe4fb43b2988d
Steven Rostedt (Red Hat) (1):
seq_buf: Fix seq_buf_vprintf() truncation
----
lib/seq_buf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) --------------------------- commit 4a8fe4e1811c96ad0ad9f4083f2fe4fb43b2988d Author: Steven Rostedt (Red Hat) <[email protected]> Date: Wed Mar 4 09:56:02 2015 -0500
seq_buf: Fix seq_buf_vprintf() truncation
In seq_buf_vprintf(), vsnprintf() is used to copy the format into the buffer remaining in the seq_buf structure. The return of vsnprintf() is the amount of characters written to the buffer excluding the '\0', unless the line was truncated!
If the line copied does not fit, it is truncated, and a '\0' is added to the end of the buffer. But in this case, '\0' is included in the length of the line written. To know if the buffer had overflowed, the return length will be the same as the length of the buffer passed in.
The check in seq_buf_vprintf() only checked if the length returned from vsnprintf() would fit in the buffer, as the seq_buf_vprintf() is only to be an all or nothing command. It either writes all the string into the seq_buf, or none of it. If the string is truncated, the pointers inside the seq_buf must be reset to what they were when the function was called. This is not the case. On overflow, it copies only part of the string.
The fix is to change the overflow check to see if the length returned from vsnprintf() is less than the length remaining in the seq_buf buffer, and not if it is less than or equal to as it currently does. Then seq_buf_vprintf() will know if the write from vsnpritnf() was truncated or not.
lib/seq_buf.c +1 -1
@@ -61,7 +61,7 @@ int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)if (s->len < s->size) { len =vsnprintf (s->buffer + s->len, s->size - s->len, fmt, args); -if (seq_buf_can_fit (s, len)) { +if (s->len + len < s->size) { s->len += len;return 0 ; }
Quoted 13 lines · Steven Rostedt
Linus,
While working on merging
seq_fileandseq_bufcode, I hit a bug and came down to a bug inseq_buf_printf(). This is suppose to be a all or nothing function. That is, either all of the string passed in is saved to theseq_bufbuffer, or none of it. But due to the wayvsnprintf()works in the kernel, if the line is truncated, the return value is the amount written to the buffer, including the '\0'. Then the test inseq_buf_printf()uses the returned length to see if it would fit in the buffer. It will, but it will also fill the buffer. The test to see ifvsnprintf()overflowed or not is to see if the length returned is equal to or greater than the length of the buffer passed to it. Not just greater than.
Quoted 1 line
Please pull the latest trace-fixes-v4.0-rc2 tree, which can be found at:
[]
Quoted 23 lines
commit
4a8fe4e1811c96ad0ad9f4083f2fe4fb43b2988dAuthor: Steven Rostedt (Red Hat) <[email protected]> Date: Wed Mar 4 09:56:02 2015 -0500
seq_buf: Fixseq_buf_vprintf()truncationIn
seq_buf_vprintf(),vsnprintf()is used to copy the format into the buffer remaining in theseq_bufstructure. The return ofvsnprintf()is the amount of characters written to the buffer excluding the '\0', unless the line was truncated!If the line copied does not fit, it is truncated, and a '\0' is added to the end of the buffer. But in this case, '\0' is included in the length of the line written. To know if the buffer had overflowed, the return length will be the same as the length of the buffer passed in.
The check in
seq_buf_vprintf()only checked if the length returned fromvsnprintf()would fit in the buffer, as theseq_buf_vprintf()is only to be an all or nothing command. It either writes all the string into theseq_buf, or none of it. If the string is truncated, the pointers inside theseq_bufmust be reset to what they were when the function was called. This is not the case. On overflow, it copies only part of the string.
Quoted 4 lines
The fix is to change the overflow check to see if the length returned from
vsnprintf()is less than the length remaining in theseq_bufbuffer, and not if it is less than or equal to as it currently does. Thenseq_buf_vprintf()will know if the write fromvsnpritnf()was truncated or not.
[]
lib/seq_buf.c
Quoted 1 line
lib/seq_buf.c +0
-0
[]
lib/seq_buf.c int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args) · line 63
Quoted 4 lines
lib/seq_buf.c +0
-0@@ -61,7 +61,7 @@ int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)if (s->len < s->size) { len =vsnprintf (s->buffer + s->len, s->size - s->len, fmt, args);
Presumably the seq_buf_bprintf function has the same issue.
Quoted 7 lines · On Wed, 04 Mar 2015 12:21:21 -0800 Joe Perches <[email protected]> wrote:
lib/seq_buf.c int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args) · line 63
Quoted 4 lines
lib/seq_buf.c +0
-0@@ -61,7 +61,7 @@ int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)if (s->len < s->size) { len =vsnprintf (s->buffer + s->len, s->size - s->len, fmt, args);Presumably the
seq_buf_bprintffunction has the same issue.
Hmm, seems so. I'll make up another patch.
-- Steve