718 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Test cases for KMSAN.4 * For each test case checks the presence (or absence) of generated reports.5 * Relies on 'console' tracepoint to capture reports as they appear in the6 * kernel log.7 *8 * Copyright (C) 2021-2022, Google LLC.9 * Author: Alexander Potapenko <glider@google.com>10 *11 */12 13#include <kunit/test.h>14#include "kmsan.h"15 16#include <linux/jiffies.h>17#include <linux/kernel.h>18#include <linux/kmsan.h>19#include <linux/mm.h>20#include <linux/random.h>21#include <linux/slab.h>22#include <linux/spinlock.h>23#include <linux/string.h>24#include <linux/tracepoint.h>25#include <linux/vmalloc.h>26#include <trace/events/printk.h>27 28static DEFINE_PER_CPU(int, per_cpu_var);29 30/* Report as observed from console. */31static struct {32 spinlock_t lock;33 bool available;34 bool ignore; /* Stop console output collection. */35 char header[256];36} observed = {37 .lock = __SPIN_LOCK_UNLOCKED(observed.lock),38};39 40/* Probe for console output: obtains observed lines of interest. */41static void probe_console(void *ignore, const char *buf, size_t len)42{43 unsigned long flags;44 45 if (observed.ignore)46 return;47 spin_lock_irqsave(&observed.lock, flags);48 49 if (strnstr(buf, "BUG: KMSAN: ", len)) {50 /*51 * KMSAN report and related to the test.52 *53 * The provided @buf is not NUL-terminated; copy no more than54 * @len bytes and let strscpy() add the missing NUL-terminator.55 */56 strscpy(observed.header, buf,57 min(len + 1, sizeof(observed.header)));58 WRITE_ONCE(observed.available, true);59 observed.ignore = true;60 }61 spin_unlock_irqrestore(&observed.lock, flags);62}63 64/* Check if a report related to the test exists. */65static bool report_available(void)66{67 return READ_ONCE(observed.available);68}69 70/* Reset observed.available, so that the test can trigger another report. */71static void report_reset(void)72{73 unsigned long flags;74 75 spin_lock_irqsave(&observed.lock, flags);76 WRITE_ONCE(observed.available, false);77 observed.ignore = false;78 spin_unlock_irqrestore(&observed.lock, flags);79}80 81/* Information we expect in a report. */82struct expect_report {83 const char *error_type; /* Error type. */84 /*85 * Kernel symbol from the error header, or NULL if no report is86 * expected.87 */88 const char *symbol;89};90 91/* Check observed report matches information in @r. */92static bool report_matches(const struct expect_report *r)93{94 typeof(observed.header) expected_header;95 unsigned long flags;96 bool ret = false;97 const char *end;98 char *cur;99 100 /* Doubled-checked locking. */101 if (!report_available() || !r->symbol)102 return (!report_available() && !r->symbol);103 104 /* Generate expected report contents. */105 106 /* Title */107 cur = expected_header;108 end = &expected_header[sizeof(expected_header) - 1];109 110 cur += scnprintf(cur, end - cur, "BUG: KMSAN: %s", r->error_type);111 112 scnprintf(cur, end - cur, " in %s", r->symbol);113 /* The exact offset won't match, remove it; also strip module name. */114 cur = strchr(expected_header, '+');115 if (cur)116 *cur = '\0';117 118 spin_lock_irqsave(&observed.lock, flags);119 if (!report_available())120 goto out; /* A new report is being captured. */121 122 /* Finally match expected output to what we actually observed. */123 ret = strstr(observed.header, expected_header);124out:125 spin_unlock_irqrestore(&observed.lock, flags);126 127 return ret;128}129 130/* ===== Test cases ===== */131 132/* Prevent replacing branch with select in LLVM. */133static noinline void check_true(char *arg)134{135 pr_info("%s is true\n", arg);136}137 138static noinline void check_false(char *arg)139{140 pr_info("%s is false\n", arg);141}142 143#define USE(x) \144 do { \145 if (x) \146 check_true(#x); \147 else \148 check_false(#x); \149 } while (0)150 151#define EXPECTATION_ETYPE_FN(e, reason, fn) \152 struct expect_report e = { \153 .error_type = reason, \154 .symbol = fn, \155 }156 157#define EXPECTATION_NO_REPORT(e) EXPECTATION_ETYPE_FN(e, NULL, NULL)158#define EXPECTATION_UNINIT_VALUE_FN(e, fn) \159 EXPECTATION_ETYPE_FN(e, "uninit-value", fn)160#define EXPECTATION_UNINIT_VALUE(e) EXPECTATION_UNINIT_VALUE_FN(e, __func__)161#define EXPECTATION_USE_AFTER_FREE(e) \162 EXPECTATION_ETYPE_FN(e, "use-after-free", __func__)163 164/* Test case: ensure that kmalloc() returns uninitialized memory. */165static void test_uninit_kmalloc(struct kunit *test)166{167 EXPECTATION_UNINIT_VALUE(expect);168 int *ptr;169 170 kunit_info(test, "uninitialized kmalloc test (UMR report)\n");171 ptr = kmalloc(sizeof(*ptr), GFP_KERNEL);172 USE(*ptr);173 KUNIT_EXPECT_TRUE(test, report_matches(&expect));174}175 176/*177 * Test case: ensure that kmalloc'ed memory becomes initialized after memset().178 */179static void test_init_kmalloc(struct kunit *test)180{181 EXPECTATION_NO_REPORT(expect);182 int *ptr;183 184 kunit_info(test, "initialized kmalloc test (no reports)\n");185 ptr = kmalloc(sizeof(*ptr), GFP_KERNEL);186 memset(ptr, 0, sizeof(*ptr));187 USE(*ptr);188 KUNIT_EXPECT_TRUE(test, report_matches(&expect));189}190 191/* Test case: ensure that kzalloc() returns initialized memory. */192static void test_init_kzalloc(struct kunit *test)193{194 EXPECTATION_NO_REPORT(expect);195 int *ptr;196 197 kunit_info(test, "initialized kzalloc test (no reports)\n");198 ptr = kzalloc(sizeof(*ptr), GFP_KERNEL);199 USE(*ptr);200 KUNIT_EXPECT_TRUE(test, report_matches(&expect));201}202 203/* Test case: ensure that local variables are uninitialized by default. */204static void test_uninit_stack_var(struct kunit *test)205{206 EXPECTATION_UNINIT_VALUE(expect);207 volatile int cond;208 209 kunit_info(test, "uninitialized stack variable (UMR report)\n");210 USE(cond);211 KUNIT_EXPECT_TRUE(test, report_matches(&expect));212}213 214/* Test case: ensure that local variables with initializers are initialized. */215static void test_init_stack_var(struct kunit *test)216{217 EXPECTATION_NO_REPORT(expect);218 volatile int cond = 1;219 220 kunit_info(test, "initialized stack variable (no reports)\n");221 USE(cond);222 KUNIT_EXPECT_TRUE(test, report_matches(&expect));223}224 225static noinline void two_param_fn_2(int arg1, int arg2)226{227 USE(arg1);228 USE(arg2);229}230 231static noinline void one_param_fn(int arg)232{233 two_param_fn_2(arg, arg);234 USE(arg);235}236 237static noinline void two_param_fn(int arg1, int arg2)238{239 int init = 0;240 241 one_param_fn(init);242 USE(arg1);243 USE(arg2);244}245 246static void test_params(struct kunit *test)247{248#ifdef CONFIG_KMSAN_CHECK_PARAM_RETVAL249 /*250 * With eager param/retval checking enabled, KMSAN will report an error251 * before the call to two_param_fn().252 */253 EXPECTATION_UNINIT_VALUE_FN(expect, "test_params");254#else255 EXPECTATION_UNINIT_VALUE_FN(expect, "two_param_fn");256#endif257 volatile int uninit, init = 1;258 259 kunit_info(test,260 "uninit passed through a function parameter (UMR report)\n");261 two_param_fn(uninit, init);262 KUNIT_EXPECT_TRUE(test, report_matches(&expect));263}264 265static int signed_sum3(int a, int b, int c)266{267 return a + b + c;268}269 270/*271 * Test case: ensure that uninitialized values are tracked through function272 * arguments.273 */274static void test_uninit_multiple_params(struct kunit *test)275{276 EXPECTATION_UNINIT_VALUE(expect);277 volatile char b = 3, c;278 volatile int a;279 280 kunit_info(test, "uninitialized local passed to fn (UMR report)\n");281 USE(signed_sum3(a, b, c));282 KUNIT_EXPECT_TRUE(test, report_matches(&expect));283}284 285/* Helper function to make an array uninitialized. */286static noinline void do_uninit_local_array(char *array, int start, int stop)287{288 volatile char uninit;289 290 for (int i = start; i < stop; i++)291 array[i] = uninit;292}293 294/*295 * Test case: ensure kmsan_check_memory() reports an error when checking296 * uninitialized memory.297 */298static void test_uninit_kmsan_check_memory(struct kunit *test)299{300 EXPECTATION_UNINIT_VALUE_FN(expect, "test_uninit_kmsan_check_memory");301 volatile char local_array[8];302 303 kunit_info(304 test,305 "kmsan_check_memory() called on uninit local (UMR report)\n");306 do_uninit_local_array((char *)local_array, 5, 7);307 308 kmsan_check_memory((char *)local_array, 8);309 KUNIT_EXPECT_TRUE(test, report_matches(&expect));310}311 312/*313 * Test case: check that a virtual memory range created with vmap() from314 * initialized pages is still considered as initialized.315 */316static void test_init_kmsan_vmap_vunmap(struct kunit *test)317{318 EXPECTATION_NO_REPORT(expect);319 const int npages = 2;320 struct page **pages;321 void *vbuf;322 323 kunit_info(test, "pages initialized via vmap (no reports)\n");324 325 pages = kmalloc_array(npages, sizeof(*pages), GFP_KERNEL);326 for (int i = 0; i < npages; i++)327 pages[i] = alloc_page(GFP_KERNEL);328 vbuf = vmap(pages, npages, VM_MAP, PAGE_KERNEL);329 memset(vbuf, 0xfe, npages * PAGE_SIZE);330 for (int i = 0; i < npages; i++)331 kmsan_check_memory(page_address(pages[i]), PAGE_SIZE);332 333 if (vbuf)334 vunmap(vbuf);335 for (int i = 0; i < npages; i++) {336 if (pages[i])337 __free_page(pages[i]);338 }339 kfree(pages);340 KUNIT_EXPECT_TRUE(test, report_matches(&expect));341}342 343/*344 * Test case: ensure that memset() can initialize a buffer allocated via345 * vmalloc().346 */347static void test_init_vmalloc(struct kunit *test)348{349 EXPECTATION_NO_REPORT(expect);350 int npages = 8;351 char *buf;352 353 kunit_info(test, "vmalloc buffer can be initialized (no reports)\n");354 buf = vmalloc(PAGE_SIZE * npages);355 buf[0] = 1;356 memset(buf, 0xfe, PAGE_SIZE * npages);357 USE(buf[0]);358 for (int i = 0; i < npages; i++)359 kmsan_check_memory(&buf[PAGE_SIZE * i], PAGE_SIZE);360 vfree(buf);361 KUNIT_EXPECT_TRUE(test, report_matches(&expect));362}363 364/* Test case: ensure that use-after-free reporting works. */365static void test_uaf(struct kunit *test)366{367 EXPECTATION_USE_AFTER_FREE(expect);368 volatile int value;369 volatile int *var;370 371 kunit_info(test, "use-after-free in kmalloc-ed buffer (UMR report)\n");372 var = kmalloc(80, GFP_KERNEL);373 var[3] = 0xfeedface;374 kfree((int *)var);375 /* Copy the invalid value before checking it. */376 value = var[3];377 USE(value);378 KUNIT_EXPECT_TRUE(test, report_matches(&expect));379}380 381/*382 * Test case: ensure that uninitialized values are propagated through per-CPU383 * memory.384 */385static void test_percpu_propagate(struct kunit *test)386{387 EXPECTATION_UNINIT_VALUE(expect);388 volatile int uninit, check;389 390 kunit_info(test,391 "uninit local stored to per_cpu memory (UMR report)\n");392 393 this_cpu_write(per_cpu_var, uninit);394 check = this_cpu_read(per_cpu_var);395 USE(check);396 KUNIT_EXPECT_TRUE(test, report_matches(&expect));397}398 399/*400 * Test case: ensure that passing uninitialized values to printk() leads to an401 * error report.402 */403static void test_printk(struct kunit *test)404{405#ifdef CONFIG_KMSAN_CHECK_PARAM_RETVAL406 /*407 * With eager param/retval checking enabled, KMSAN will report an error408 * before the call to pr_info().409 */410 EXPECTATION_UNINIT_VALUE_FN(expect, "test_printk");411#else412 EXPECTATION_UNINIT_VALUE_FN(expect, "number");413#endif414 volatile int uninit;415 416 kunit_info(test, "uninit local passed to pr_info() (UMR report)\n");417 pr_info("%px contains %d\n", &uninit, uninit);418 KUNIT_EXPECT_TRUE(test, report_matches(&expect));419}420 421/* Prevent the compiler from inlining a memcpy() call. */422static noinline void *memcpy_noinline(volatile void *dst,423 const volatile void *src, size_t size)424{425 return memcpy((void *)dst, (const void *)src, size);426}427 428/* Test case: ensure that memcpy() correctly copies initialized values. */429static void test_init_memcpy(struct kunit *test)430{431 EXPECTATION_NO_REPORT(expect);432 volatile long long src;433 volatile long long dst = 0;434 435 src = 1;436 kunit_info(437 test,438 "memcpy()ing aligned initialized src to aligned dst (no reports)\n");439 memcpy_noinline((void *)&dst, (void *)&src, sizeof(src));440 kmsan_check_memory((void *)&dst, sizeof(dst));441 KUNIT_EXPECT_TRUE(test, report_matches(&expect));442}443 444/*445 * Test case: ensure that memcpy() correctly copies uninitialized values between446 * aligned `src` and `dst`.447 */448static void test_memcpy_aligned_to_aligned(struct kunit *test)449{450 EXPECTATION_UNINIT_VALUE_FN(expect, "test_memcpy_aligned_to_aligned");451 volatile int uninit_src;452 volatile int dst = 0;453 454 kunit_info(455 test,456 "memcpy()ing aligned uninit src to aligned dst (UMR report)\n");457 memcpy_noinline((void *)&dst, (void *)&uninit_src, sizeof(uninit_src));458 kmsan_check_memory((void *)&dst, sizeof(dst));459 KUNIT_EXPECT_TRUE(test, report_matches(&expect));460}461 462/*463 * Test case: ensure that memcpy() correctly copies uninitialized values between464 * aligned `src` and unaligned `dst`.465 *466 * Copying aligned 4-byte value to an unaligned one leads to touching two467 * aligned 4-byte values. This test case checks that KMSAN correctly reports an468 * error on the mentioned two values.469 */470static void test_memcpy_aligned_to_unaligned(struct kunit *test)471{472 EXPECTATION_UNINIT_VALUE_FN(expect, "test_memcpy_aligned_to_unaligned");473 volatile int uninit_src;474 volatile char dst[8] = { 0 };475 476 kunit_info(477 test,478 "memcpy()ing aligned uninit src to unaligned dst (UMR report)\n");479 kmsan_check_memory((void *)&uninit_src, sizeof(uninit_src));480 memcpy_noinline((void *)&dst[1], (void *)&uninit_src,481 sizeof(uninit_src));482 kmsan_check_memory((void *)dst, 4);483 KUNIT_EXPECT_TRUE(test, report_matches(&expect));484 report_reset();485 kmsan_check_memory((void *)&dst[4], sizeof(uninit_src));486 KUNIT_EXPECT_TRUE(test, report_matches(&expect));487}488 489/*490 * Test case: ensure that origin slots do not accidentally get overwritten with491 * zeroes during memcpy().492 *493 * Previously, when copying memory from an aligned buffer to an unaligned one,494 * if there were zero origins corresponding to zero shadow values in the source495 * buffer, they could have ended up being copied to nonzero shadow values in the496 * destination buffer:497 *498 * memcpy(0xffff888080a00000, 0xffff888080900002, 8)499 *500 * src (0xffff888080900002): ..xx .... xx..501 * src origins: o111 0000 o222502 * dst (0xffff888080a00000): xx.. ..xx503 * dst origins: o111 0000504 * (or 0000 o222)505 *506 * (here . stands for an initialized byte, and x for an uninitialized one.507 *508 * Ensure that this does not happen anymore, and for both destination bytes509 * the origin is nonzero (i.e. KMSAN reports an error).510 */511static void test_memcpy_initialized_gap(struct kunit *test)512{513 EXPECTATION_UNINIT_VALUE_FN(expect, "test_memcpy_initialized_gap");514 volatile char uninit_src[12];515 volatile char dst[8] = { 0 };516 517 kunit_info(518 test,519 "unaligned 4-byte initialized value gets a nonzero origin after memcpy() - (2 UMR reports)\n");520 521 uninit_src[0] = 42;522 uninit_src[1] = 42;523 uninit_src[4] = 42;524 uninit_src[5] = 42;525 uninit_src[6] = 42;526 uninit_src[7] = 42;527 uninit_src[10] = 42;528 uninit_src[11] = 42;529 memcpy_noinline((void *)&dst[0], (void *)&uninit_src[2], 8);530 531 kmsan_check_memory((void *)&dst[0], 4);532 KUNIT_EXPECT_TRUE(test, report_matches(&expect));533 report_reset();534 kmsan_check_memory((void *)&dst[2], 4);535 KUNIT_EXPECT_FALSE(test, report_matches(&expect));536 report_reset();537 kmsan_check_memory((void *)&dst[4], 4);538 KUNIT_EXPECT_TRUE(test, report_matches(&expect));539}540 541/* Generate test cases for memset16(), memset32(), memset64(). */542#define DEFINE_TEST_MEMSETXX(size) \543 static void test_memset##size(struct kunit *test) \544 { \545 EXPECTATION_NO_REPORT(expect); \546 volatile uint##size##_t uninit; \547 \548 kunit_info(test, \549 "memset" #size "() should initialize memory\n"); \550 memset##size((uint##size##_t *)&uninit, 0, 1); \551 kmsan_check_memory((void *)&uninit, sizeof(uninit)); \552 KUNIT_EXPECT_TRUE(test, report_matches(&expect)); \553 }554 555DEFINE_TEST_MEMSETXX(16)556DEFINE_TEST_MEMSETXX(32)557DEFINE_TEST_MEMSETXX(64)558 559static noinline void fibonacci(int *array, int size, int start)560{561 if (start < 2 || (start == size))562 return;563 array[start] = array[start - 1] + array[start - 2];564 fibonacci(array, size, start + 1);565}566 567static void test_long_origin_chain(struct kunit *test)568{569 EXPECTATION_UNINIT_VALUE_FN(expect, "test_long_origin_chain");570 /* (KMSAN_MAX_ORIGIN_DEPTH * 2) recursive calls to fibonacci(). */571 volatile int accum[KMSAN_MAX_ORIGIN_DEPTH * 2 + 2];572 int last = ARRAY_SIZE(accum) - 1;573 574 kunit_info(575 test,576 "origin chain exceeding KMSAN_MAX_ORIGIN_DEPTH (UMR report)\n");577 /*578 * We do not set accum[1] to 0, so the uninitializedness will be carried579 * over to accum[2..last].580 */581 accum[0] = 1;582 fibonacci((int *)accum, ARRAY_SIZE(accum), 2);583 kmsan_check_memory((void *)&accum[last], sizeof(int));584 KUNIT_EXPECT_TRUE(test, report_matches(&expect));585}586 587/*588 * Test case: ensure that saving/restoring/printing stacks to/from stackdepot589 * does not trigger errors.590 *591 * KMSAN uses stackdepot to store origin stack traces, that's why we do not592 * instrument lib/stackdepot.c. Yet it must properly mark its outputs as593 * initialized because other kernel features (e.g. netdev tracker) may also594 * access stackdepot from instrumented code.595 */596static void test_stackdepot_roundtrip(struct kunit *test)597{598 unsigned long src_entries[16], *dst_entries;599 unsigned int src_nentries, dst_nentries;600 EXPECTATION_NO_REPORT(expect);601 depot_stack_handle_t handle;602 603 kunit_info(test, "testing stackdepot roundtrip (no reports)\n");604 605 src_nentries =606 stack_trace_save(src_entries, ARRAY_SIZE(src_entries), 1);607 handle = stack_depot_save(src_entries, src_nentries, GFP_KERNEL);608 stack_depot_print(handle);609 dst_nentries = stack_depot_fetch(handle, &dst_entries);610 KUNIT_EXPECT_TRUE(test, src_nentries == dst_nentries);611 612 kmsan_check_memory((void *)dst_entries,613 sizeof(*dst_entries) * dst_nentries);614 KUNIT_EXPECT_TRUE(test, report_matches(&expect));615}616 617/*618 * Test case: ensure that kmsan_unpoison_memory() and the instrumentation work619 * the same.620 */621static void test_unpoison_memory(struct kunit *test)622{623 EXPECTATION_UNINIT_VALUE_FN(expect, "test_unpoison_memory");624 volatile char a[4], b[4];625 626 kunit_info(627 test,628 "unpoisoning via the instrumentation vs. kmsan_unpoison_memory() (2 UMR reports)\n");629 630 /* Initialize a[0] and check a[1]--a[3]. */631 a[0] = 0;632 kmsan_check_memory((char *)&a[1], 3);633 KUNIT_EXPECT_TRUE(test, report_matches(&expect));634 635 report_reset();636 637 /* Initialize b[0] and check b[1]--b[3]. */638 kmsan_unpoison_memory((char *)&b[0], 1);639 kmsan_check_memory((char *)&b[1], 3);640 KUNIT_EXPECT_TRUE(test, report_matches(&expect));641}642 643static struct kunit_case kmsan_test_cases[] = {644 KUNIT_CASE(test_uninit_kmalloc),645 KUNIT_CASE(test_init_kmalloc),646 KUNIT_CASE(test_init_kzalloc),647 KUNIT_CASE(test_uninit_stack_var),648 KUNIT_CASE(test_init_stack_var),649 KUNIT_CASE(test_params),650 KUNIT_CASE(test_uninit_multiple_params),651 KUNIT_CASE(test_uninit_kmsan_check_memory),652 KUNIT_CASE(test_init_kmsan_vmap_vunmap),653 KUNIT_CASE(test_init_vmalloc),654 KUNIT_CASE(test_uaf),655 KUNIT_CASE(test_percpu_propagate),656 KUNIT_CASE(test_printk),657 KUNIT_CASE(test_init_memcpy),658 KUNIT_CASE(test_memcpy_aligned_to_aligned),659 KUNIT_CASE(test_memcpy_aligned_to_unaligned),660 KUNIT_CASE(test_memcpy_initialized_gap),661 KUNIT_CASE(test_memset16),662 KUNIT_CASE(test_memset32),663 KUNIT_CASE(test_memset64),664 KUNIT_CASE(test_long_origin_chain),665 KUNIT_CASE(test_stackdepot_roundtrip),666 KUNIT_CASE(test_unpoison_memory),667 {},668};669 670/* ===== End test cases ===== */671 672static int test_init(struct kunit *test)673{674 unsigned long flags;675 676 spin_lock_irqsave(&observed.lock, flags);677 observed.header[0] = '\0';678 observed.ignore = false;679 observed.available = false;680 spin_unlock_irqrestore(&observed.lock, flags);681 682 return 0;683}684 685static void test_exit(struct kunit *test)686{687}688 689static int orig_panic_on_kmsan;690 691static int kmsan_suite_init(struct kunit_suite *suite)692{693 register_trace_console(probe_console, NULL);694 orig_panic_on_kmsan = panic_on_kmsan;695 panic_on_kmsan = 0;696 return 0;697}698 699static void kmsan_suite_exit(struct kunit_suite *suite)700{701 unregister_trace_console(probe_console, NULL);702 tracepoint_synchronize_unregister();703 panic_on_kmsan = orig_panic_on_kmsan;704}705 706static struct kunit_suite kmsan_test_suite = {707 .name = "kmsan",708 .test_cases = kmsan_test_cases,709 .init = test_init,710 .exit = test_exit,711 .suite_init = kmsan_suite_init,712 .suite_exit = kmsan_suite_exit,713};714kunit_test_suites(&kmsan_test_suite);715 716MODULE_LICENSE("GPL");717MODULE_AUTHOR("Alexander Potapenko <glider@google.com>");718