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