diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/seq_buf.c | 17 | ||||
| -rw-r--r-- | lib/tests/seq_buf_kunit.c | 85 |
2 files changed, 98 insertions, 4 deletions
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), {} }; |
