summaryrefslogtreecommitdiff
path: root/lib/tests
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tests')
-rw-r--r--lib/tests/seq_buf_kunit.c537
-rw-r--r--lib/tests/stackinit_kunit.c93
2 files changed, 601 insertions, 29 deletions
diff --git a/lib/tests/seq_buf_kunit.c b/lib/tests/seq_buf_kunit.c
index eb466386bbef..fc53e9ef844c 100644
--- a/lib/tests/seq_buf_kunit.c
+++ b/lib/tests/seq_buf_kunit.c
@@ -6,7 +6,11 @@
*/
#include <kunit/test.h>
+#include <linux/console.h>
+#include <linux/fs.h>
#include <linux/seq_buf.h>
+#include <linux/shmem_fs.h>
+#include <linux/string.h>
static void seq_buf_init_test(struct kunit *test)
{
@@ -21,6 +25,75 @@ 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_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);
+ /* Overflowed at the size; an unbounded scan would find more. */
+ KUNIT_EXPECT_EQ(test, s.len, 5 + 1);
+ 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)
@@ -113,6 +186,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);
@@ -205,9 +319,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);
@@ -216,18 +330,437 @@ 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
+ * 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);
+}
+
+/* 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 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 void seq_buf_terminate_test(struct kunit *test)
+{
+ char buf[16];
+ struct seq_buf s;
+
+ /*
+ * seq_buf_putmem() writes no NUL, so only seq_buf_terminate() can
+ * end these strings early. The NUL in the last byte of buf keeps
+ * the comparisons in bounds if it does not.
+ */
+
+ /* Terminates directly after the data when there is room. */
+ memset(buf, 'z', sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ seq_buf_init(&s, buf, sizeof(buf));
+ seq_buf_putmem(&s, "ab", 2);
+ 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) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ seq_buf_init(&s, buf, 4);
+ seq_buf_putmem(&s, "abcdef", 6);
+ 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_init_append_test),
KUNIT_CASE(seq_buf_declare_test),
KUNIT_CASE(seq_buf_clear_test),
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),
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_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_terminate_test),
+ KUNIT_CASE(seq_buf_do_printk_test),
{}
};
diff --git a/lib/tests/stackinit_kunit.c b/lib/tests/stackinit_kunit.c
index ff2784769772..d589536afa20 100644
--- a/lib/tests/stackinit_kunit.c
+++ b/lib/tests/stackinit_kunit.c
@@ -369,17 +369,24 @@ union test_small_end {
char four;
};
-#define ALWAYS_PASS WANT_SUCCESS
-#define ALWAYS_FAIL XFAIL
+#define ALWAYS_PASS WANT_SUCCESS
+#define ALWAYS_FAIL XFAIL
#ifdef CONFIG_INIT_STACK_NONE
-# define USER_PASS XFAIL
-# define BYREF_PASS XFAIL
-# define STRONG_PASS XFAIL
+# define INSTRUMENTED_PASS XFAIL
#else
-# define USER_PASS WANT_SUCCESS
-# define BYREF_PASS WANT_SUCCESS
-# define STRONG_PASS WANT_SUCCESS
+# define INSTRUMENTED_PASS WANT_SUCCESS
+#endif
+
+/*
+ * Clang 24 and later emit initialization on the jumps that bypass the
+ * declaration. GCC still initializes only at the declaration. Track
+ * test expectation based on CONFIG_INIT_STACK_NONE above.
+ */
+#if defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION >= 240000
+# define BYPASS_PASS INSTRUMENTED_PASS
+#else
+# define BYPASS_PASS XFAIL
#endif
#define DEFINE_SCALAR_TEST(name, init, xfail) \
@@ -432,23 +439,23 @@ DEFINE_STRUCT_TESTS(old_zero, ALWAYS_PASS);
DEFINE_UNION_TESTS(zero, ALWAYS_PASS);
DEFINE_UNION_TESTS(old_zero, ALWAYS_PASS);
/* Struct initializers: padding may be left uninitialized. */
-DEFINE_STRUCT_INITIALIZER_TESTS(static, STRONG_PASS);
-DEFINE_STRUCT_INITIALIZER_TESTS(dynamic, STRONG_PASS);
-DEFINE_STRUCT_INITIALIZER_TESTS(runtime, STRONG_PASS);
-DEFINE_STRUCT_INITIALIZER_TESTS(assigned_static, STRONG_PASS);
-DEFINE_STRUCT_INITIALIZER_TESTS(assigned_dynamic, STRONG_PASS);
+DEFINE_STRUCT_INITIALIZER_TESTS(static, INSTRUMENTED_PASS);
+DEFINE_STRUCT_INITIALIZER_TESTS(dynamic, INSTRUMENTED_PASS);
+DEFINE_STRUCT_INITIALIZER_TESTS(runtime, INSTRUMENTED_PASS);
+DEFINE_STRUCT_INITIALIZER_TESTS(assigned_static, INSTRUMENTED_PASS);
+DEFINE_STRUCT_INITIALIZER_TESTS(assigned_dynamic, INSTRUMENTED_PASS);
DEFINE_STRUCT_TESTS(assigned_copy, ALWAYS_FAIL);
-DEFINE_UNION_INITIALIZER_TESTS(static, STRONG_PASS);
-DEFINE_UNION_INITIALIZER_TESTS(dynamic, STRONG_PASS);
-DEFINE_UNION_INITIALIZER_TESTS(runtime, STRONG_PASS);
-DEFINE_UNION_INITIALIZER_TESTS(assigned_static, STRONG_PASS);
-DEFINE_UNION_INITIALIZER_TESTS(assigned_dynamic, STRONG_PASS);
+DEFINE_UNION_INITIALIZER_TESTS(static, INSTRUMENTED_PASS);
+DEFINE_UNION_INITIALIZER_TESTS(dynamic, INSTRUMENTED_PASS);
+DEFINE_UNION_INITIALIZER_TESTS(runtime, INSTRUMENTED_PASS);
+DEFINE_UNION_INITIALIZER_TESTS(assigned_static, INSTRUMENTED_PASS);
+DEFINE_UNION_INITIALIZER_TESTS(assigned_dynamic, INSTRUMENTED_PASS);
DEFINE_UNION_TESTS(assigned_copy, ALWAYS_FAIL);
/* No initialization without compiler instrumentation. */
-DEFINE_SCALAR_TESTS(none, STRONG_PASS);
-DEFINE_STRUCT_TESTS(none, BYREF_PASS);
+DEFINE_SCALAR_TESTS(none, INSTRUMENTED_PASS);
+DEFINE_STRUCT_TESTS(none, INSTRUMENTED_PASS);
/* Initialization of members with __user attribute. */
-DEFINE_TEST(user, struct test_user, STRUCT, none, USER_PASS);
+DEFINE_TEST(user, struct test_user, STRUCT, none, INSTRUMENTED_PASS);
/*
* Check two uses through a variable declaration outside either path,
@@ -493,6 +500,36 @@ static int noinline __leaf_switch_none(int path, bool fill)
return 0;
}
+static noinline int __leaf_goto_none(bool fill)
+{
+ goto bypass;
+ /*
+ * This declaration is jumped over by the goto above, so it is
+ * never reached. Compilers that initialize only at the point of
+ * declaration leave this variable untouched.
+ */
+ uint64_t var[10];
+
+bypass:
+ target_start = &var;
+ target_size = sizeof(var);
+ if (fill) {
+ fill_start = &var;
+ fill_size = sizeof(var);
+
+ memset(fill_start, (forced_mask | 0x55) & FILL_BYTE, fill_size);
+ }
+ memcpy(check_buf, target_start, target_size);
+
+ return 0;
+}
+
+static noinline int leaf_goto_none(unsigned long sp, bool fill,
+ uint64_t *arg)
+{
+ return __leaf_goto_none(fill);
+}
+
static noinline int leaf_switch_1_none(unsigned long sp, bool fill,
uint64_t *arg)
{
@@ -506,13 +543,14 @@ static noinline int leaf_switch_2_none(unsigned long sp, bool fill,
}
/*
- * These are expected to fail for most configurations because neither
- * GCC nor Clang have a way to perform initialization of variables in
- * non-code areas (i.e. in a switch statement before the first "case").
- * https://llvm.org/pr44916
+ * A declaration whose definition point is jumped over, either by a switch
+ * dispatch reaching a "case" label below it or by a goto, used to be left
+ * uninitialized: the initialization is emitted where the variable is
+ * declared, and that point is never reached.
*/
-DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, ALWAYS_FAIL);
-DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, ALWAYS_FAIL);
+DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, BYPASS_PASS);
+DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, BYPASS_PASS);
+DEFINE_TEST_DRIVER(goto_none, uint64_t, SCALAR, BYPASS_PASS);
#define KUNIT_test_scalars(init) \
KUNIT_CASE(test_u8_ ## init), \
@@ -568,6 +606,7 @@ static struct kunit_case stackinit_test_cases[] = {
KUNIT_test_scalars(none),
KUNIT_CASE(test_switch_1_none),
KUNIT_CASE(test_switch_2_none),
+ KUNIT_CASE(test_goto_none),
/* STRUCTLEAK_BYREF should cover from here down. */
KUNIT_test_structs(none),
/* STRUCTLEAK will only cover this. */