Merge tag 'trace-v6.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux...
[platform/kernel/linux-starfive.git] / mm / kmsan / kmsan_test.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
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 the
6  * 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 <trace/events/printk.h>
26
27 static DEFINE_PER_CPU(int, per_cpu_var);
28
29 /* Report as observed from console. */
30 static struct {
31         spinlock_t lock;
32         bool available;
33         bool ignore; /* Stop console output collection. */
34         char header[256];
35 } observed = {
36         .lock = __SPIN_LOCK_UNLOCKED(observed.lock),
37 };
38
39 /* Probe for console output: obtains observed lines of interest. */
40 static void probe_console(void *ignore, const char *buf, size_t len)
41 {
42         unsigned long flags;
43
44         if (observed.ignore)
45                 return;
46         spin_lock_irqsave(&observed.lock, flags);
47
48         if (strnstr(buf, "BUG: KMSAN: ", len)) {
49                 /*
50                  * KMSAN report and related to the test.
51                  *
52                  * The provided @buf is not NUL-terminated; copy no more than
53                  * @len bytes and let strscpy() add the missing NUL-terminator.
54                  */
55                 strscpy(observed.header, buf,
56                         min(len + 1, sizeof(observed.header)));
57                 WRITE_ONCE(observed.available, true);
58                 observed.ignore = true;
59         }
60         spin_unlock_irqrestore(&observed.lock, flags);
61 }
62
63 /* Check if a report related to the test exists. */
64 static bool report_available(void)
65 {
66         return READ_ONCE(observed.available);
67 }
68
69 /* Information we expect in a report. */
70 struct expect_report {
71         const char *error_type; /* Error type. */
72         /*
73          * Kernel symbol from the error header, or NULL if no report is
74          * expected.
75          */
76         const char *symbol;
77 };
78
79 /* Check observed report matches information in @r. */
80 static bool report_matches(const struct expect_report *r)
81 {
82         typeof(observed.header) expected_header;
83         unsigned long flags;
84         bool ret = false;
85         const char *end;
86         char *cur;
87
88         /* Doubled-checked locking. */
89         if (!report_available() || !r->symbol)
90                 return (!report_available() && !r->symbol);
91
92         /* Generate expected report contents. */
93
94         /* Title */
95         cur = expected_header;
96         end = &expected_header[sizeof(expected_header) - 1];
97
98         cur += scnprintf(cur, end - cur, "BUG: KMSAN: %s", r->error_type);
99
100         scnprintf(cur, end - cur, " in %s", r->symbol);
101         /* The exact offset won't match, remove it; also strip module name. */
102         cur = strchr(expected_header, '+');
103         if (cur)
104                 *cur = '\0';
105
106         spin_lock_irqsave(&observed.lock, flags);
107         if (!report_available())
108                 goto out; /* A new report is being captured. */
109
110         /* Finally match expected output to what we actually observed. */
111         ret = strstr(observed.header, expected_header);
112 out:
113         spin_unlock_irqrestore(&observed.lock, flags);
114
115         return ret;
116 }
117
118 /* ===== Test cases ===== */
119
120 /* Prevent replacing branch with select in LLVM. */
121 static noinline void check_true(char *arg)
122 {
123         pr_info("%s is true\n", arg);
124 }
125
126 static noinline void check_false(char *arg)
127 {
128         pr_info("%s is false\n", arg);
129 }
130
131 #define USE(x)                           \
132         do {                             \
133                 if (x)                   \
134                         check_true(#x);  \
135                 else                     \
136                         check_false(#x); \
137         } while (0)
138
139 #define EXPECTATION_ETYPE_FN(e, reason, fn) \
140         struct expect_report e = {          \
141                 .error_type = reason,       \
142                 .symbol = fn,               \
143         }
144
145 #define EXPECTATION_NO_REPORT(e) EXPECTATION_ETYPE_FN(e, NULL, NULL)
146 #define EXPECTATION_UNINIT_VALUE_FN(e, fn) \
147         EXPECTATION_ETYPE_FN(e, "uninit-value", fn)
148 #define EXPECTATION_UNINIT_VALUE(e) EXPECTATION_UNINIT_VALUE_FN(e, __func__)
149 #define EXPECTATION_USE_AFTER_FREE(e) \
150         EXPECTATION_ETYPE_FN(e, "use-after-free", __func__)
151
152 /* Test case: ensure that kmalloc() returns uninitialized memory. */
153 static void test_uninit_kmalloc(struct kunit *test)
154 {
155         EXPECTATION_UNINIT_VALUE(expect);
156         int *ptr;
157
158         kunit_info(test, "uninitialized kmalloc test (UMR report)\n");
159         ptr = kmalloc(sizeof(*ptr), GFP_KERNEL);
160         USE(*ptr);
161         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
162 }
163
164 /*
165  * Test case: ensure that kmalloc'ed memory becomes initialized after memset().
166  */
167 static void test_init_kmalloc(struct kunit *test)
168 {
169         EXPECTATION_NO_REPORT(expect);
170         int *ptr;
171
172         kunit_info(test, "initialized kmalloc test (no reports)\n");
173         ptr = kmalloc(sizeof(*ptr), GFP_KERNEL);
174         memset(ptr, 0, sizeof(*ptr));
175         USE(*ptr);
176         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
177 }
178
179 /* Test case: ensure that kzalloc() returns initialized memory. */
180 static void test_init_kzalloc(struct kunit *test)
181 {
182         EXPECTATION_NO_REPORT(expect);
183         int *ptr;
184
185         kunit_info(test, "initialized kzalloc test (no reports)\n");
186         ptr = kzalloc(sizeof(*ptr), GFP_KERNEL);
187         USE(*ptr);
188         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
189 }
190
191 /* Test case: ensure that local variables are uninitialized by default. */
192 static void test_uninit_stack_var(struct kunit *test)
193 {
194         EXPECTATION_UNINIT_VALUE(expect);
195         volatile int cond;
196
197         kunit_info(test, "uninitialized stack variable (UMR report)\n");
198         USE(cond);
199         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
200 }
201
202 /* Test case: ensure that local variables with initializers are initialized. */
203 static void test_init_stack_var(struct kunit *test)
204 {
205         EXPECTATION_NO_REPORT(expect);
206         volatile int cond = 1;
207
208         kunit_info(test, "initialized stack variable (no reports)\n");
209         USE(cond);
210         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
211 }
212
213 static noinline void two_param_fn_2(int arg1, int arg2)
214 {
215         USE(arg1);
216         USE(arg2);
217 }
218
219 static noinline void one_param_fn(int arg)
220 {
221         two_param_fn_2(arg, arg);
222         USE(arg);
223 }
224
225 static noinline void two_param_fn(int arg1, int arg2)
226 {
227         int init = 0;
228
229         one_param_fn(init);
230         USE(arg1);
231         USE(arg2);
232 }
233
234 static void test_params(struct kunit *test)
235 {
236 #ifdef CONFIG_KMSAN_CHECK_PARAM_RETVAL
237         /*
238          * With eager param/retval checking enabled, KMSAN will report an error
239          * before the call to two_param_fn().
240          */
241         EXPECTATION_UNINIT_VALUE_FN(expect, "test_params");
242 #else
243         EXPECTATION_UNINIT_VALUE_FN(expect, "two_param_fn");
244 #endif
245         volatile int uninit, init = 1;
246
247         kunit_info(test,
248                    "uninit passed through a function parameter (UMR report)\n");
249         two_param_fn(uninit, init);
250         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
251 }
252
253 static int signed_sum3(int a, int b, int c)
254 {
255         return a + b + c;
256 }
257
258 /*
259  * Test case: ensure that uninitialized values are tracked through function
260  * arguments.
261  */
262 static void test_uninit_multiple_params(struct kunit *test)
263 {
264         EXPECTATION_UNINIT_VALUE(expect);
265         volatile char b = 3, c;
266         volatile int a;
267
268         kunit_info(test, "uninitialized local passed to fn (UMR report)\n");
269         USE(signed_sum3(a, b, c));
270         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
271 }
272
273 /* Helper function to make an array uninitialized. */
274 static noinline void do_uninit_local_array(char *array, int start, int stop)
275 {
276         volatile char uninit;
277
278         for (int i = start; i < stop; i++)
279                 array[i] = uninit;
280 }
281
282 /*
283  * Test case: ensure kmsan_check_memory() reports an error when checking
284  * uninitialized memory.
285  */
286 static void test_uninit_kmsan_check_memory(struct kunit *test)
287 {
288         EXPECTATION_UNINIT_VALUE_FN(expect, "test_uninit_kmsan_check_memory");
289         volatile char local_array[8];
290
291         kunit_info(
292                 test,
293                 "kmsan_check_memory() called on uninit local (UMR report)\n");
294         do_uninit_local_array((char *)local_array, 5, 7);
295
296         kmsan_check_memory((char *)local_array, 8);
297         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
298 }
299
300 /*
301  * Test case: check that a virtual memory range created with vmap() from
302  * initialized pages is still considered as initialized.
303  */
304 static void test_init_kmsan_vmap_vunmap(struct kunit *test)
305 {
306         EXPECTATION_NO_REPORT(expect);
307         const int npages = 2;
308         struct page **pages;
309         void *vbuf;
310
311         kunit_info(test, "pages initialized via vmap (no reports)\n");
312
313         pages = kmalloc_array(npages, sizeof(*pages), GFP_KERNEL);
314         for (int i = 0; i < npages; i++)
315                 pages[i] = alloc_page(GFP_KERNEL);
316         vbuf = vmap(pages, npages, VM_MAP, PAGE_KERNEL);
317         memset(vbuf, 0xfe, npages * PAGE_SIZE);
318         for (int i = 0; i < npages; i++)
319                 kmsan_check_memory(page_address(pages[i]), PAGE_SIZE);
320
321         if (vbuf)
322                 vunmap(vbuf);
323         for (int i = 0; i < npages; i++) {
324                 if (pages[i])
325                         __free_page(pages[i]);
326         }
327         kfree(pages);
328         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
329 }
330
331 /*
332  * Test case: ensure that memset() can initialize a buffer allocated via
333  * vmalloc().
334  */
335 static void test_init_vmalloc(struct kunit *test)
336 {
337         EXPECTATION_NO_REPORT(expect);
338         int npages = 8;
339         char *buf;
340
341         kunit_info(test, "vmalloc buffer can be initialized (no reports)\n");
342         buf = vmalloc(PAGE_SIZE * npages);
343         buf[0] = 1;
344         memset(buf, 0xfe, PAGE_SIZE * npages);
345         USE(buf[0]);
346         for (int i = 0; i < npages; i++)
347                 kmsan_check_memory(&buf[PAGE_SIZE * i], PAGE_SIZE);
348         vfree(buf);
349         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
350 }
351
352 /* Test case: ensure that use-after-free reporting works. */
353 static void test_uaf(struct kunit *test)
354 {
355         EXPECTATION_USE_AFTER_FREE(expect);
356         volatile int value;
357         volatile int *var;
358
359         kunit_info(test, "use-after-free in kmalloc-ed buffer (UMR report)\n");
360         var = kmalloc(80, GFP_KERNEL);
361         var[3] = 0xfeedface;
362         kfree((int *)var);
363         /* Copy the invalid value before checking it. */
364         value = var[3];
365         USE(value);
366         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
367 }
368
369 /*
370  * Test case: ensure that uninitialized values are propagated through per-CPU
371  * memory.
372  */
373 static void test_percpu_propagate(struct kunit *test)
374 {
375         EXPECTATION_UNINIT_VALUE(expect);
376         volatile int uninit, check;
377
378         kunit_info(test,
379                    "uninit local stored to per_cpu memory (UMR report)\n");
380
381         this_cpu_write(per_cpu_var, uninit);
382         check = this_cpu_read(per_cpu_var);
383         USE(check);
384         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
385 }
386
387 /*
388  * Test case: ensure that passing uninitialized values to printk() leads to an
389  * error report.
390  */
391 static void test_printk(struct kunit *test)
392 {
393 #ifdef CONFIG_KMSAN_CHECK_PARAM_RETVAL
394         /*
395          * With eager param/retval checking enabled, KMSAN will report an error
396          * before the call to pr_info().
397          */
398         EXPECTATION_UNINIT_VALUE_FN(expect, "test_printk");
399 #else
400         EXPECTATION_UNINIT_VALUE_FN(expect, "number");
401 #endif
402         volatile int uninit;
403
404         kunit_info(test, "uninit local passed to pr_info() (UMR report)\n");
405         pr_info("%px contains %d\n", &uninit, uninit);
406         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
407 }
408
409 /*
410  * Test case: ensure that memcpy() correctly copies uninitialized values between
411  * aligned `src` and `dst`.
412  */
413 static void test_memcpy_aligned_to_aligned(struct kunit *test)
414 {
415         EXPECTATION_UNINIT_VALUE_FN(expect, "test_memcpy_aligned_to_aligned");
416         volatile int uninit_src;
417         volatile int dst = 0;
418
419         kunit_info(
420                 test,
421                 "memcpy()ing aligned uninit src to aligned dst (UMR report)\n");
422         OPTIMIZER_HIDE_VAR(uninit_src);
423         memcpy((void *)&dst, (void *)&uninit_src, sizeof(uninit_src));
424         kmsan_check_memory((void *)&dst, sizeof(dst));
425         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
426 }
427
428 /*
429  * Test case: ensure that memcpy() correctly copies uninitialized values between
430  * aligned `src` and unaligned `dst`.
431  *
432  * Copying aligned 4-byte value to an unaligned one leads to touching two
433  * aligned 4-byte values. This test case checks that KMSAN correctly reports an
434  * error on the first of the two values.
435  */
436 static void test_memcpy_aligned_to_unaligned(struct kunit *test)
437 {
438         EXPECTATION_UNINIT_VALUE_FN(expect, "test_memcpy_aligned_to_unaligned");
439         volatile int uninit_src;
440         volatile char dst[8] = { 0 };
441
442         kunit_info(
443                 test,
444                 "memcpy()ing aligned uninit src to unaligned dst (UMR report)\n");
445         OPTIMIZER_HIDE_VAR(uninit_src);
446         memcpy((void *)&dst[1], (void *)&uninit_src, sizeof(uninit_src));
447         kmsan_check_memory((void *)dst, 4);
448         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
449 }
450
451 /*
452  * Test case: ensure that memcpy() correctly copies uninitialized values between
453  * aligned `src` and unaligned `dst`.
454  *
455  * Copying aligned 4-byte value to an unaligned one leads to touching two
456  * aligned 4-byte values. This test case checks that KMSAN correctly reports an
457  * error on the second of the two values.
458  */
459 static void test_memcpy_aligned_to_unaligned2(struct kunit *test)
460 {
461         EXPECTATION_UNINIT_VALUE_FN(expect,
462                                     "test_memcpy_aligned_to_unaligned2");
463         volatile int uninit_src;
464         volatile char dst[8] = { 0 };
465
466         kunit_info(
467                 test,
468                 "memcpy()ing aligned uninit src to unaligned dst - part 2 (UMR report)\n");
469         OPTIMIZER_HIDE_VAR(uninit_src);
470         memcpy((void *)&dst[1], (void *)&uninit_src, sizeof(uninit_src));
471         kmsan_check_memory((void *)&dst[4], sizeof(uninit_src));
472         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
473 }
474
475 static noinline void fibonacci(int *array, int size, int start) {
476         if (start < 2 || (start == size))
477                 return;
478         array[start] = array[start - 1] + array[start - 2];
479         fibonacci(array, size, start + 1);
480 }
481
482 static void test_long_origin_chain(struct kunit *test)
483 {
484         EXPECTATION_UNINIT_VALUE_FN(expect,
485                                     "test_long_origin_chain");
486         /* (KMSAN_MAX_ORIGIN_DEPTH * 2) recursive calls to fibonacci(). */
487         volatile int accum[KMSAN_MAX_ORIGIN_DEPTH * 2 + 2];
488         int last = ARRAY_SIZE(accum) - 1;
489
490         kunit_info(
491                 test,
492                 "origin chain exceeding KMSAN_MAX_ORIGIN_DEPTH (UMR report)\n");
493         /*
494          * We do not set accum[1] to 0, so the uninitializedness will be carried
495          * over to accum[2..last].
496          */
497         accum[0] = 1;
498         fibonacci((int *)accum, ARRAY_SIZE(accum), 2);
499         kmsan_check_memory((void *)&accum[last], sizeof(int));
500         KUNIT_EXPECT_TRUE(test, report_matches(&expect));
501 }
502
503 static struct kunit_case kmsan_test_cases[] = {
504         KUNIT_CASE(test_uninit_kmalloc),
505         KUNIT_CASE(test_init_kmalloc),
506         KUNIT_CASE(test_init_kzalloc),
507         KUNIT_CASE(test_uninit_stack_var),
508         KUNIT_CASE(test_init_stack_var),
509         KUNIT_CASE(test_params),
510         KUNIT_CASE(test_uninit_multiple_params),
511         KUNIT_CASE(test_uninit_kmsan_check_memory),
512         KUNIT_CASE(test_init_kmsan_vmap_vunmap),
513         KUNIT_CASE(test_init_vmalloc),
514         KUNIT_CASE(test_uaf),
515         KUNIT_CASE(test_percpu_propagate),
516         KUNIT_CASE(test_printk),
517         KUNIT_CASE(test_memcpy_aligned_to_aligned),
518         KUNIT_CASE(test_memcpy_aligned_to_unaligned),
519         KUNIT_CASE(test_memcpy_aligned_to_unaligned2),
520         KUNIT_CASE(test_long_origin_chain),
521         {},
522 };
523
524 /* ===== End test cases ===== */
525
526 static int test_init(struct kunit *test)
527 {
528         unsigned long flags;
529
530         spin_lock_irqsave(&observed.lock, flags);
531         observed.header[0] = '\0';
532         observed.ignore = false;
533         observed.available = false;
534         spin_unlock_irqrestore(&observed.lock, flags);
535
536         return 0;
537 }
538
539 static void test_exit(struct kunit *test)
540 {
541 }
542
543 static void register_tracepoints(struct tracepoint *tp, void *ignore)
544 {
545         check_trace_callback_type_console(probe_console);
546         if (!strcmp(tp->name, "console"))
547                 WARN_ON(tracepoint_probe_register(tp, probe_console, NULL));
548 }
549
550 static void unregister_tracepoints(struct tracepoint *tp, void *ignore)
551 {
552         if (!strcmp(tp->name, "console"))
553                 tracepoint_probe_unregister(tp, probe_console, NULL);
554 }
555
556 static int kmsan_suite_init(struct kunit_suite *suite)
557 {
558         /*
559          * Because we want to be able to build the test as a module, we need to
560          * iterate through all known tracepoints, since the static registration
561          * won't work here.
562          */
563         for_each_kernel_tracepoint(register_tracepoints, NULL);
564         return 0;
565 }
566
567 static void kmsan_suite_exit(struct kunit_suite *suite)
568 {
569         for_each_kernel_tracepoint(unregister_tracepoints, NULL);
570         tracepoint_synchronize_unregister();
571 }
572
573 static struct kunit_suite kmsan_test_suite = {
574         .name = "kmsan",
575         .test_cases = kmsan_test_cases,
576         .init = test_init,
577         .exit = test_exit,
578         .suite_init = kmsan_suite_init,
579         .suite_exit = kmsan_suite_exit,
580 };
581 kunit_test_suites(&kmsan_test_suite);
582
583 MODULE_LICENSE("GPL");
584 MODULE_AUTHOR("Alexander Potapenko <glider@google.com>");