162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Test cases for KMSAN. 462306a36Sopenharmony_ci * For each test case checks the presence (or absence) of generated reports. 562306a36Sopenharmony_ci * Relies on 'console' tracepoint to capture reports as they appear in the 662306a36Sopenharmony_ci * kernel log. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2021-2022, Google LLC. 962306a36Sopenharmony_ci * Author: Alexander Potapenko <glider@google.com> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <kunit/test.h> 1462306a36Sopenharmony_ci#include "kmsan.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/jiffies.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/kmsan.h> 1962306a36Sopenharmony_ci#include <linux/mm.h> 2062306a36Sopenharmony_ci#include <linux/random.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/spinlock.h> 2362306a36Sopenharmony_ci#include <linux/string.h> 2462306a36Sopenharmony_ci#include <linux/tracepoint.h> 2562306a36Sopenharmony_ci#include <linux/vmalloc.h> 2662306a36Sopenharmony_ci#include <trace/events/printk.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic DEFINE_PER_CPU(int, per_cpu_var); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Report as observed from console. */ 3162306a36Sopenharmony_cistatic struct { 3262306a36Sopenharmony_ci spinlock_t lock; 3362306a36Sopenharmony_ci bool available; 3462306a36Sopenharmony_ci bool ignore; /* Stop console output collection. */ 3562306a36Sopenharmony_ci char header[256]; 3662306a36Sopenharmony_ci} observed = { 3762306a36Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(observed.lock), 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* Probe for console output: obtains observed lines of interest. */ 4162306a36Sopenharmony_cistatic void probe_console(void *ignore, const char *buf, size_t len) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci unsigned long flags; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (observed.ignore) 4662306a36Sopenharmony_ci return; 4762306a36Sopenharmony_ci spin_lock_irqsave(&observed.lock, flags); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (strnstr(buf, "BUG: KMSAN: ", len)) { 5062306a36Sopenharmony_ci /* 5162306a36Sopenharmony_ci * KMSAN report and related to the test. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * The provided @buf is not NUL-terminated; copy no more than 5462306a36Sopenharmony_ci * @len bytes and let strscpy() add the missing NUL-terminator. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci strscpy(observed.header, buf, 5762306a36Sopenharmony_ci min(len + 1, sizeof(observed.header))); 5862306a36Sopenharmony_ci WRITE_ONCE(observed.available, true); 5962306a36Sopenharmony_ci observed.ignore = true; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci spin_unlock_irqrestore(&observed.lock, flags); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Check if a report related to the test exists. */ 6562306a36Sopenharmony_cistatic bool report_available(void) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci return READ_ONCE(observed.available); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* Information we expect in a report. */ 7162306a36Sopenharmony_cistruct expect_report { 7262306a36Sopenharmony_ci const char *error_type; /* Error type. */ 7362306a36Sopenharmony_ci /* 7462306a36Sopenharmony_ci * Kernel symbol from the error header, or NULL if no report is 7562306a36Sopenharmony_ci * expected. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci const char *symbol; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* Check observed report matches information in @r. */ 8162306a36Sopenharmony_cistatic bool report_matches(const struct expect_report *r) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci typeof(observed.header) expected_header; 8462306a36Sopenharmony_ci unsigned long flags; 8562306a36Sopenharmony_ci bool ret = false; 8662306a36Sopenharmony_ci const char *end; 8762306a36Sopenharmony_ci char *cur; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Doubled-checked locking. */ 9062306a36Sopenharmony_ci if (!report_available() || !r->symbol) 9162306a36Sopenharmony_ci return (!report_available() && !r->symbol); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* Generate expected report contents. */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* Title */ 9662306a36Sopenharmony_ci cur = expected_header; 9762306a36Sopenharmony_ci end = &expected_header[sizeof(expected_header) - 1]; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci cur += scnprintf(cur, end - cur, "BUG: KMSAN: %s", r->error_type); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci scnprintf(cur, end - cur, " in %s", r->symbol); 10262306a36Sopenharmony_ci /* The exact offset won't match, remove it; also strip module name. */ 10362306a36Sopenharmony_ci cur = strchr(expected_header, '+'); 10462306a36Sopenharmony_ci if (cur) 10562306a36Sopenharmony_ci *cur = '\0'; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci spin_lock_irqsave(&observed.lock, flags); 10862306a36Sopenharmony_ci if (!report_available()) 10962306a36Sopenharmony_ci goto out; /* A new report is being captured. */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* Finally match expected output to what we actually observed. */ 11262306a36Sopenharmony_ci ret = strstr(observed.header, expected_header); 11362306a36Sopenharmony_ciout: 11462306a36Sopenharmony_ci spin_unlock_irqrestore(&observed.lock, flags); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return ret; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* ===== Test cases ===== */ 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* Prevent replacing branch with select in LLVM. */ 12262306a36Sopenharmony_cistatic noinline void check_true(char *arg) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci pr_info("%s is true\n", arg); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic noinline void check_false(char *arg) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci pr_info("%s is false\n", arg); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define USE(x) \ 13362306a36Sopenharmony_ci do { \ 13462306a36Sopenharmony_ci if (x) \ 13562306a36Sopenharmony_ci check_true(#x); \ 13662306a36Sopenharmony_ci else \ 13762306a36Sopenharmony_ci check_false(#x); \ 13862306a36Sopenharmony_ci } while (0) 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci#define EXPECTATION_ETYPE_FN(e, reason, fn) \ 14162306a36Sopenharmony_ci struct expect_report e = { \ 14262306a36Sopenharmony_ci .error_type = reason, \ 14362306a36Sopenharmony_ci .symbol = fn, \ 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#define EXPECTATION_NO_REPORT(e) EXPECTATION_ETYPE_FN(e, NULL, NULL) 14762306a36Sopenharmony_ci#define EXPECTATION_UNINIT_VALUE_FN(e, fn) \ 14862306a36Sopenharmony_ci EXPECTATION_ETYPE_FN(e, "uninit-value", fn) 14962306a36Sopenharmony_ci#define EXPECTATION_UNINIT_VALUE(e) EXPECTATION_UNINIT_VALUE_FN(e, __func__) 15062306a36Sopenharmony_ci#define EXPECTATION_USE_AFTER_FREE(e) \ 15162306a36Sopenharmony_ci EXPECTATION_ETYPE_FN(e, "use-after-free", __func__) 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* Test case: ensure that kmalloc() returns uninitialized memory. */ 15462306a36Sopenharmony_cistatic void test_uninit_kmalloc(struct kunit *test) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci EXPECTATION_UNINIT_VALUE(expect); 15762306a36Sopenharmony_ci int *ptr; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci kunit_info(test, "uninitialized kmalloc test (UMR report)\n"); 16062306a36Sopenharmony_ci ptr = kmalloc(sizeof(*ptr), GFP_KERNEL); 16162306a36Sopenharmony_ci USE(*ptr); 16262306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* 16662306a36Sopenharmony_ci * Test case: ensure that kmalloc'ed memory becomes initialized after memset(). 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_cistatic void test_init_kmalloc(struct kunit *test) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci EXPECTATION_NO_REPORT(expect); 17162306a36Sopenharmony_ci int *ptr; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci kunit_info(test, "initialized kmalloc test (no reports)\n"); 17462306a36Sopenharmony_ci ptr = kmalloc(sizeof(*ptr), GFP_KERNEL); 17562306a36Sopenharmony_ci memset(ptr, 0, sizeof(*ptr)); 17662306a36Sopenharmony_ci USE(*ptr); 17762306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* Test case: ensure that kzalloc() returns initialized memory. */ 18162306a36Sopenharmony_cistatic void test_init_kzalloc(struct kunit *test) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci EXPECTATION_NO_REPORT(expect); 18462306a36Sopenharmony_ci int *ptr; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci kunit_info(test, "initialized kzalloc test (no reports)\n"); 18762306a36Sopenharmony_ci ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); 18862306a36Sopenharmony_ci USE(*ptr); 18962306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* Test case: ensure that local variables are uninitialized by default. */ 19362306a36Sopenharmony_cistatic void test_uninit_stack_var(struct kunit *test) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci EXPECTATION_UNINIT_VALUE(expect); 19662306a36Sopenharmony_ci volatile int cond; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci kunit_info(test, "uninitialized stack variable (UMR report)\n"); 19962306a36Sopenharmony_ci USE(cond); 20062306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci/* Test case: ensure that local variables with initializers are initialized. */ 20462306a36Sopenharmony_cistatic void test_init_stack_var(struct kunit *test) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci EXPECTATION_NO_REPORT(expect); 20762306a36Sopenharmony_ci volatile int cond = 1; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci kunit_info(test, "initialized stack variable (no reports)\n"); 21062306a36Sopenharmony_ci USE(cond); 21162306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic noinline void two_param_fn_2(int arg1, int arg2) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci USE(arg1); 21762306a36Sopenharmony_ci USE(arg2); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic noinline void one_param_fn(int arg) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci two_param_fn_2(arg, arg); 22362306a36Sopenharmony_ci USE(arg); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic noinline void two_param_fn(int arg1, int arg2) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci int init = 0; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci one_param_fn(init); 23162306a36Sopenharmony_ci USE(arg1); 23262306a36Sopenharmony_ci USE(arg2); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void test_params(struct kunit *test) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci#ifdef CONFIG_KMSAN_CHECK_PARAM_RETVAL 23862306a36Sopenharmony_ci /* 23962306a36Sopenharmony_ci * With eager param/retval checking enabled, KMSAN will report an error 24062306a36Sopenharmony_ci * before the call to two_param_fn(). 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci EXPECTATION_UNINIT_VALUE_FN(expect, "test_params"); 24362306a36Sopenharmony_ci#else 24462306a36Sopenharmony_ci EXPECTATION_UNINIT_VALUE_FN(expect, "two_param_fn"); 24562306a36Sopenharmony_ci#endif 24662306a36Sopenharmony_ci volatile int uninit, init = 1; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci kunit_info(test, 24962306a36Sopenharmony_ci "uninit passed through a function parameter (UMR report)\n"); 25062306a36Sopenharmony_ci two_param_fn(uninit, init); 25162306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int signed_sum3(int a, int b, int c) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci return a + b + c; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci/* 26062306a36Sopenharmony_ci * Test case: ensure that uninitialized values are tracked through function 26162306a36Sopenharmony_ci * arguments. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistatic void test_uninit_multiple_params(struct kunit *test) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci EXPECTATION_UNINIT_VALUE(expect); 26662306a36Sopenharmony_ci volatile char b = 3, c; 26762306a36Sopenharmony_ci volatile int a; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci kunit_info(test, "uninitialized local passed to fn (UMR report)\n"); 27062306a36Sopenharmony_ci USE(signed_sum3(a, b, c)); 27162306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/* Helper function to make an array uninitialized. */ 27562306a36Sopenharmony_cistatic noinline void do_uninit_local_array(char *array, int start, int stop) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci volatile char uninit; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci for (int i = start; i < stop; i++) 28062306a36Sopenharmony_ci array[i] = uninit; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci/* 28462306a36Sopenharmony_ci * Test case: ensure kmsan_check_memory() reports an error when checking 28562306a36Sopenharmony_ci * uninitialized memory. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_cistatic void test_uninit_kmsan_check_memory(struct kunit *test) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci EXPECTATION_UNINIT_VALUE_FN(expect, "test_uninit_kmsan_check_memory"); 29062306a36Sopenharmony_ci volatile char local_array[8]; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci kunit_info( 29362306a36Sopenharmony_ci test, 29462306a36Sopenharmony_ci "kmsan_check_memory() called on uninit local (UMR report)\n"); 29562306a36Sopenharmony_ci do_uninit_local_array((char *)local_array, 5, 7); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci kmsan_check_memory((char *)local_array, 8); 29862306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* 30262306a36Sopenharmony_ci * Test case: check that a virtual memory range created with vmap() from 30362306a36Sopenharmony_ci * initialized pages is still considered as initialized. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_cistatic void test_init_kmsan_vmap_vunmap(struct kunit *test) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci EXPECTATION_NO_REPORT(expect); 30862306a36Sopenharmony_ci const int npages = 2; 30962306a36Sopenharmony_ci struct page **pages; 31062306a36Sopenharmony_ci void *vbuf; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci kunit_info(test, "pages initialized via vmap (no reports)\n"); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci pages = kmalloc_array(npages, sizeof(*pages), GFP_KERNEL); 31562306a36Sopenharmony_ci for (int i = 0; i < npages; i++) 31662306a36Sopenharmony_ci pages[i] = alloc_page(GFP_KERNEL); 31762306a36Sopenharmony_ci vbuf = vmap(pages, npages, VM_MAP, PAGE_KERNEL); 31862306a36Sopenharmony_ci memset(vbuf, 0xfe, npages * PAGE_SIZE); 31962306a36Sopenharmony_ci for (int i = 0; i < npages; i++) 32062306a36Sopenharmony_ci kmsan_check_memory(page_address(pages[i]), PAGE_SIZE); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (vbuf) 32362306a36Sopenharmony_ci vunmap(vbuf); 32462306a36Sopenharmony_ci for (int i = 0; i < npages; i++) { 32562306a36Sopenharmony_ci if (pages[i]) 32662306a36Sopenharmony_ci __free_page(pages[i]); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci kfree(pages); 32962306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/* 33362306a36Sopenharmony_ci * Test case: ensure that memset() can initialize a buffer allocated via 33462306a36Sopenharmony_ci * vmalloc(). 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_cistatic void test_init_vmalloc(struct kunit *test) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci EXPECTATION_NO_REPORT(expect); 33962306a36Sopenharmony_ci int npages = 8; 34062306a36Sopenharmony_ci char *buf; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci kunit_info(test, "vmalloc buffer can be initialized (no reports)\n"); 34362306a36Sopenharmony_ci buf = vmalloc(PAGE_SIZE * npages); 34462306a36Sopenharmony_ci buf[0] = 1; 34562306a36Sopenharmony_ci memset(buf, 0xfe, PAGE_SIZE * npages); 34662306a36Sopenharmony_ci USE(buf[0]); 34762306a36Sopenharmony_ci for (int i = 0; i < npages; i++) 34862306a36Sopenharmony_ci kmsan_check_memory(&buf[PAGE_SIZE * i], PAGE_SIZE); 34962306a36Sopenharmony_ci vfree(buf); 35062306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci/* Test case: ensure that use-after-free reporting works. */ 35462306a36Sopenharmony_cistatic void test_uaf(struct kunit *test) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci EXPECTATION_USE_AFTER_FREE(expect); 35762306a36Sopenharmony_ci volatile int value; 35862306a36Sopenharmony_ci volatile int *var; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci kunit_info(test, "use-after-free in kmalloc-ed buffer (UMR report)\n"); 36162306a36Sopenharmony_ci var = kmalloc(80, GFP_KERNEL); 36262306a36Sopenharmony_ci var[3] = 0xfeedface; 36362306a36Sopenharmony_ci kfree((int *)var); 36462306a36Sopenharmony_ci /* Copy the invalid value before checking it. */ 36562306a36Sopenharmony_ci value = var[3]; 36662306a36Sopenharmony_ci USE(value); 36762306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/* 37162306a36Sopenharmony_ci * Test case: ensure that uninitialized values are propagated through per-CPU 37262306a36Sopenharmony_ci * memory. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_cistatic void test_percpu_propagate(struct kunit *test) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci EXPECTATION_UNINIT_VALUE(expect); 37762306a36Sopenharmony_ci volatile int uninit, check; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci kunit_info(test, 38062306a36Sopenharmony_ci "uninit local stored to per_cpu memory (UMR report)\n"); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci this_cpu_write(per_cpu_var, uninit); 38362306a36Sopenharmony_ci check = this_cpu_read(per_cpu_var); 38462306a36Sopenharmony_ci USE(check); 38562306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci/* 38962306a36Sopenharmony_ci * Test case: ensure that passing uninitialized values to printk() leads to an 39062306a36Sopenharmony_ci * error report. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_cistatic void test_printk(struct kunit *test) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci#ifdef CONFIG_KMSAN_CHECK_PARAM_RETVAL 39562306a36Sopenharmony_ci /* 39662306a36Sopenharmony_ci * With eager param/retval checking enabled, KMSAN will report an error 39762306a36Sopenharmony_ci * before the call to pr_info(). 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_ci EXPECTATION_UNINIT_VALUE_FN(expect, "test_printk"); 40062306a36Sopenharmony_ci#else 40162306a36Sopenharmony_ci EXPECTATION_UNINIT_VALUE_FN(expect, "number"); 40262306a36Sopenharmony_ci#endif 40362306a36Sopenharmony_ci volatile int uninit; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci kunit_info(test, "uninit local passed to pr_info() (UMR report)\n"); 40662306a36Sopenharmony_ci pr_info("%px contains %d\n", &uninit, uninit); 40762306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* 41162306a36Sopenharmony_ci * Prevent the compiler from optimizing @var away. Without this, Clang may 41262306a36Sopenharmony_ci * notice that @var is uninitialized and drop memcpy() calls that use it. 41362306a36Sopenharmony_ci * 41462306a36Sopenharmony_ci * There is OPTIMIZER_HIDE_VAR() in linux/compier.h that we cannot use here, 41562306a36Sopenharmony_ci * because it is implemented as inline assembly receiving @var as a parameter 41662306a36Sopenharmony_ci * and will enforce a KMSAN check. Same is true for e.g. barrier_data(var). 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci#define DO_NOT_OPTIMIZE(var) barrier() 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci/* 42162306a36Sopenharmony_ci * Test case: ensure that memcpy() correctly copies initialized values. 42262306a36Sopenharmony_ci * Also serves as a regression test to ensure DO_NOT_OPTIMIZE() does not cause 42362306a36Sopenharmony_ci * extra checks. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_cistatic void test_init_memcpy(struct kunit *test) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci EXPECTATION_NO_REPORT(expect); 42862306a36Sopenharmony_ci volatile int src; 42962306a36Sopenharmony_ci volatile int dst = 0; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci DO_NOT_OPTIMIZE(src); 43262306a36Sopenharmony_ci src = 1; 43362306a36Sopenharmony_ci kunit_info( 43462306a36Sopenharmony_ci test, 43562306a36Sopenharmony_ci "memcpy()ing aligned initialized src to aligned dst (no reports)\n"); 43662306a36Sopenharmony_ci memcpy((void *)&dst, (void *)&src, sizeof(src)); 43762306a36Sopenharmony_ci kmsan_check_memory((void *)&dst, sizeof(dst)); 43862306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci/* 44262306a36Sopenharmony_ci * Test case: ensure that memcpy() correctly copies uninitialized values between 44362306a36Sopenharmony_ci * aligned `src` and `dst`. 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_cistatic void test_memcpy_aligned_to_aligned(struct kunit *test) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci EXPECTATION_UNINIT_VALUE_FN(expect, "test_memcpy_aligned_to_aligned"); 44862306a36Sopenharmony_ci volatile int uninit_src; 44962306a36Sopenharmony_ci volatile int dst = 0; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci kunit_info( 45262306a36Sopenharmony_ci test, 45362306a36Sopenharmony_ci "memcpy()ing aligned uninit src to aligned dst (UMR report)\n"); 45462306a36Sopenharmony_ci DO_NOT_OPTIMIZE(uninit_src); 45562306a36Sopenharmony_ci memcpy((void *)&dst, (void *)&uninit_src, sizeof(uninit_src)); 45662306a36Sopenharmony_ci kmsan_check_memory((void *)&dst, sizeof(dst)); 45762306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci/* 46162306a36Sopenharmony_ci * Test case: ensure that memcpy() correctly copies uninitialized values between 46262306a36Sopenharmony_ci * aligned `src` and unaligned `dst`. 46362306a36Sopenharmony_ci * 46462306a36Sopenharmony_ci * Copying aligned 4-byte value to an unaligned one leads to touching two 46562306a36Sopenharmony_ci * aligned 4-byte values. This test case checks that KMSAN correctly reports an 46662306a36Sopenharmony_ci * error on the first of the two values. 46762306a36Sopenharmony_ci */ 46862306a36Sopenharmony_cistatic void test_memcpy_aligned_to_unaligned(struct kunit *test) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci EXPECTATION_UNINIT_VALUE_FN(expect, "test_memcpy_aligned_to_unaligned"); 47162306a36Sopenharmony_ci volatile int uninit_src; 47262306a36Sopenharmony_ci volatile char dst[8] = { 0 }; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci kunit_info( 47562306a36Sopenharmony_ci test, 47662306a36Sopenharmony_ci "memcpy()ing aligned uninit src to unaligned dst (UMR report)\n"); 47762306a36Sopenharmony_ci DO_NOT_OPTIMIZE(uninit_src); 47862306a36Sopenharmony_ci memcpy((void *)&dst[1], (void *)&uninit_src, sizeof(uninit_src)); 47962306a36Sopenharmony_ci kmsan_check_memory((void *)dst, 4); 48062306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci/* 48462306a36Sopenharmony_ci * Test case: ensure that memcpy() correctly copies uninitialized values between 48562306a36Sopenharmony_ci * aligned `src` and unaligned `dst`. 48662306a36Sopenharmony_ci * 48762306a36Sopenharmony_ci * Copying aligned 4-byte value to an unaligned one leads to touching two 48862306a36Sopenharmony_ci * aligned 4-byte values. This test case checks that KMSAN correctly reports an 48962306a36Sopenharmony_ci * error on the second of the two values. 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_cistatic void test_memcpy_aligned_to_unaligned2(struct kunit *test) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci EXPECTATION_UNINIT_VALUE_FN(expect, 49462306a36Sopenharmony_ci "test_memcpy_aligned_to_unaligned2"); 49562306a36Sopenharmony_ci volatile int uninit_src; 49662306a36Sopenharmony_ci volatile char dst[8] = { 0 }; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci kunit_info( 49962306a36Sopenharmony_ci test, 50062306a36Sopenharmony_ci "memcpy()ing aligned uninit src to unaligned dst - part 2 (UMR report)\n"); 50162306a36Sopenharmony_ci DO_NOT_OPTIMIZE(uninit_src); 50262306a36Sopenharmony_ci memcpy((void *)&dst[1], (void *)&uninit_src, sizeof(uninit_src)); 50362306a36Sopenharmony_ci kmsan_check_memory((void *)&dst[4], sizeof(uninit_src)); 50462306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci/* Generate test cases for memset16(), memset32(), memset64(). */ 50862306a36Sopenharmony_ci#define DEFINE_TEST_MEMSETXX(size) \ 50962306a36Sopenharmony_ci static void test_memset##size(struct kunit *test) \ 51062306a36Sopenharmony_ci { \ 51162306a36Sopenharmony_ci EXPECTATION_NO_REPORT(expect); \ 51262306a36Sopenharmony_ci volatile uint##size##_t uninit; \ 51362306a36Sopenharmony_ci \ 51462306a36Sopenharmony_ci kunit_info(test, \ 51562306a36Sopenharmony_ci "memset" #size "() should initialize memory\n"); \ 51662306a36Sopenharmony_ci DO_NOT_OPTIMIZE(uninit); \ 51762306a36Sopenharmony_ci memset##size((uint##size##_t *)&uninit, 0, 1); \ 51862306a36Sopenharmony_ci kmsan_check_memory((void *)&uninit, sizeof(uninit)); \ 51962306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); \ 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ciDEFINE_TEST_MEMSETXX(16) 52362306a36Sopenharmony_ciDEFINE_TEST_MEMSETXX(32) 52462306a36Sopenharmony_ciDEFINE_TEST_MEMSETXX(64) 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic noinline void fibonacci(int *array, int size, int start) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci if (start < 2 || (start == size)) 52962306a36Sopenharmony_ci return; 53062306a36Sopenharmony_ci array[start] = array[start - 1] + array[start - 2]; 53162306a36Sopenharmony_ci fibonacci(array, size, start + 1); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic void test_long_origin_chain(struct kunit *test) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci EXPECTATION_UNINIT_VALUE_FN(expect, "test_long_origin_chain"); 53762306a36Sopenharmony_ci /* (KMSAN_MAX_ORIGIN_DEPTH * 2) recursive calls to fibonacci(). */ 53862306a36Sopenharmony_ci volatile int accum[KMSAN_MAX_ORIGIN_DEPTH * 2 + 2]; 53962306a36Sopenharmony_ci int last = ARRAY_SIZE(accum) - 1; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci kunit_info( 54262306a36Sopenharmony_ci test, 54362306a36Sopenharmony_ci "origin chain exceeding KMSAN_MAX_ORIGIN_DEPTH (UMR report)\n"); 54462306a36Sopenharmony_ci /* 54562306a36Sopenharmony_ci * We do not set accum[1] to 0, so the uninitializedness will be carried 54662306a36Sopenharmony_ci * over to accum[2..last]. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci accum[0] = 1; 54962306a36Sopenharmony_ci fibonacci((int *)accum, ARRAY_SIZE(accum), 2); 55062306a36Sopenharmony_ci kmsan_check_memory((void *)&accum[last], sizeof(int)); 55162306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/* 55562306a36Sopenharmony_ci * Test case: ensure that saving/restoring/printing stacks to/from stackdepot 55662306a36Sopenharmony_ci * does not trigger errors. 55762306a36Sopenharmony_ci * 55862306a36Sopenharmony_ci * KMSAN uses stackdepot to store origin stack traces, that's why we do not 55962306a36Sopenharmony_ci * instrument lib/stackdepot.c. Yet it must properly mark its outputs as 56062306a36Sopenharmony_ci * initialized because other kernel features (e.g. netdev tracker) may also 56162306a36Sopenharmony_ci * access stackdepot from instrumented code. 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_cistatic void test_stackdepot_roundtrip(struct kunit *test) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci unsigned long src_entries[16], *dst_entries; 56662306a36Sopenharmony_ci unsigned int src_nentries, dst_nentries; 56762306a36Sopenharmony_ci EXPECTATION_NO_REPORT(expect); 56862306a36Sopenharmony_ci depot_stack_handle_t handle; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci kunit_info(test, "testing stackdepot roundtrip (no reports)\n"); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci src_nentries = 57362306a36Sopenharmony_ci stack_trace_save(src_entries, ARRAY_SIZE(src_entries), 1); 57462306a36Sopenharmony_ci handle = stack_depot_save(src_entries, src_nentries, GFP_KERNEL); 57562306a36Sopenharmony_ci stack_depot_print(handle); 57662306a36Sopenharmony_ci dst_nentries = stack_depot_fetch(handle, &dst_entries); 57762306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, src_nentries == dst_nentries); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci kmsan_check_memory((void *)dst_entries, 58062306a36Sopenharmony_ci sizeof(*dst_entries) * dst_nentries); 58162306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, report_matches(&expect)); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic struct kunit_case kmsan_test_cases[] = { 58562306a36Sopenharmony_ci KUNIT_CASE(test_uninit_kmalloc), 58662306a36Sopenharmony_ci KUNIT_CASE(test_init_kmalloc), 58762306a36Sopenharmony_ci KUNIT_CASE(test_init_kzalloc), 58862306a36Sopenharmony_ci KUNIT_CASE(test_uninit_stack_var), 58962306a36Sopenharmony_ci KUNIT_CASE(test_init_stack_var), 59062306a36Sopenharmony_ci KUNIT_CASE(test_params), 59162306a36Sopenharmony_ci KUNIT_CASE(test_uninit_multiple_params), 59262306a36Sopenharmony_ci KUNIT_CASE(test_uninit_kmsan_check_memory), 59362306a36Sopenharmony_ci KUNIT_CASE(test_init_kmsan_vmap_vunmap), 59462306a36Sopenharmony_ci KUNIT_CASE(test_init_vmalloc), 59562306a36Sopenharmony_ci KUNIT_CASE(test_uaf), 59662306a36Sopenharmony_ci KUNIT_CASE(test_percpu_propagate), 59762306a36Sopenharmony_ci KUNIT_CASE(test_printk), 59862306a36Sopenharmony_ci KUNIT_CASE(test_init_memcpy), 59962306a36Sopenharmony_ci KUNIT_CASE(test_memcpy_aligned_to_aligned), 60062306a36Sopenharmony_ci KUNIT_CASE(test_memcpy_aligned_to_unaligned), 60162306a36Sopenharmony_ci KUNIT_CASE(test_memcpy_aligned_to_unaligned2), 60262306a36Sopenharmony_ci KUNIT_CASE(test_memset16), 60362306a36Sopenharmony_ci KUNIT_CASE(test_memset32), 60462306a36Sopenharmony_ci KUNIT_CASE(test_memset64), 60562306a36Sopenharmony_ci KUNIT_CASE(test_long_origin_chain), 60662306a36Sopenharmony_ci KUNIT_CASE(test_stackdepot_roundtrip), 60762306a36Sopenharmony_ci {}, 60862306a36Sopenharmony_ci}; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci/* ===== End test cases ===== */ 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic int test_init(struct kunit *test) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci unsigned long flags; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci spin_lock_irqsave(&observed.lock, flags); 61762306a36Sopenharmony_ci observed.header[0] = '\0'; 61862306a36Sopenharmony_ci observed.ignore = false; 61962306a36Sopenharmony_ci observed.available = false; 62062306a36Sopenharmony_ci spin_unlock_irqrestore(&observed.lock, flags); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci return 0; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic void test_exit(struct kunit *test) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic int kmsan_suite_init(struct kunit_suite *suite) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci register_trace_console(probe_console, NULL); 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic void kmsan_suite_exit(struct kunit_suite *suite) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci unregister_trace_console(probe_console, NULL); 63862306a36Sopenharmony_ci tracepoint_synchronize_unregister(); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic struct kunit_suite kmsan_test_suite = { 64262306a36Sopenharmony_ci .name = "kmsan", 64362306a36Sopenharmony_ci .test_cases = kmsan_test_cases, 64462306a36Sopenharmony_ci .init = test_init, 64562306a36Sopenharmony_ci .exit = test_exit, 64662306a36Sopenharmony_ci .suite_init = kmsan_suite_init, 64762306a36Sopenharmony_ci .suite_exit = kmsan_suite_exit, 64862306a36Sopenharmony_ci}; 64962306a36Sopenharmony_cikunit_test_suites(&kmsan_test_suite); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 65262306a36Sopenharmony_ciMODULE_AUTHOR("Alexander Potapenko <glider@google.com>"); 653