summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/seq_buf.c17
-rw-r--r--lib/tests/seq_buf_kunit.c85
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),
{}
};