diff options
Diffstat (limited to 'lib/tests')
| -rw-r--r-- | lib/tests/seq_buf_kunit.c | 537 | ||||
| -rw-r--r-- | lib/tests/stackinit_kunit.c | 93 |
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. */ |
