History

kmsan: add tests for KMSAN

The testing module triggers KMSAN warnings in different cases and checks that the errors are properly reported, using console probes to capture the tool's output.

Alexander Potapenko committed 4 years ago

Commit 8ed691b1 parent 553a8013 files · +597 −0Browse files

Unified · Split

3 files
  1. lib/Kconfig.kmsan +12 −0
  2. mm/kmsan/Makefile +4 −0
  3. mm/kmsan/kmsan_test.c +581 −0
lib/Kconfig.kmsan+12−0
@@ -47,4 +47,16 @@ config KMSAN_CHECK_PARAM_RETVAL · 46 unmodified lines
47 may potentially report errors in corner cases when non-instrumented47 may potentially report errors in corner cases when non-instrumented
48 functions call instrumented ones.48 functions call instrumented ones.
49 49
50config KMSAN_KUNIT_TEST
51 tristate "KMSAN integration test suite" if !KUNIT_ALL_TESTS
52 default KUNIT_ALL_TESTS
53 depends on TRACEPOINTS && KUNIT
54 help
55 Test suite for KMSAN, testing various error detection scenarios,
56 and checking that reports are correctly output to console.
57
58 Say Y here if you want the test to be built into the kernel and run
59 during boot; say M if you want the test to build as a module; say N
60 if you are unsure.
61
50endif62endif
mm/kmsan/Makefile+4−0
@@ -22,3 +22,7 @@ CFLAGS_init.o := $(CC_FLAGS_KMSAN_RUNTIME) · 21 unmodified lines
22CFLAGS_instrumentation.o := $(CC_FLAGS_KMSAN_RUNTIME)22CFLAGS_instrumentation.o := $(CC_FLAGS_KMSAN_RUNTIME)
23CFLAGS_report.o := $(CC_FLAGS_KMSAN_RUNTIME)23CFLAGS_report.o := $(CC_FLAGS_KMSAN_RUNTIME)
24CFLAGS_shadow.o := $(CC_FLAGS_KMSAN_RUNTIME)24CFLAGS_shadow.o := $(CC_FLAGS_KMSAN_RUNTIME)
25
26obj-$(CONFIG_KMSAN_KUNIT_TEST) += kmsan_test.o
27KMSAN_SANITIZE_kmsan_test.o := y
28CFLAGS_kmsan_test.o += $(call cc-disable-warning, uninitialized)
mm/kmsan/kmsan_test.c+581−0
@@ -0,0 +1,581 @@
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
27static DEFINE_PER_CPU(int, per_cpu_var);
28
29/* Report as observed from console. */
30static 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. */
40static 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. */
64static bool report_available(void)
65{
66 return READ_ONCE(observed.available);
67}
68
69/* Information we expect in a report. */
70struct 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. */
80static 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);
112out:
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. */
121static noinline void check_true(char *arg)
122{
123 pr_info("%s is true\n", arg);
124}
125
126static 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. */
153static 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 */
167static 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. */
180static 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. */
192static 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. */
203static 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
213static noinline void two_param_fn_2(int arg1, int arg2)
214{
215 USE(arg1);
216 USE(arg2);
217}
218
219static noinline void one_param_fn(int arg)
220{
221 two_param_fn_2(arg, arg);
222 USE(arg);
223}
224
225static 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
234static 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
253static 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 */
262static 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. */
274static 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 */
286static 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 */
304static 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 */
335static 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. */
353static 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 */
373static 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 */
391static 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 */
413static 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 memcpy((void *)&dst, (void *)&uninit_src, sizeof(uninit_src));
423 kmsan_check_memory((void *)&dst, sizeof(dst));
424 KUNIT_EXPECT_TRUE(test, report_matches(&expect));
425}
426
427/*
428 * Test case: ensure that memcpy() correctly copies uninitialized values between
429 * aligned `src` and unaligned `dst`.
430 *
431 * Copying aligned 4-byte value to an unaligned one leads to touching two
432 * aligned 4-byte values. This test case checks that KMSAN correctly reports an
433 * error on the first of the two values.
434 */
435static void test_memcpy_aligned_to_unaligned(struct kunit *test)
436{
437 EXPECTATION_UNINIT_VALUE_FN(expect, "test_memcpy_aligned_to_unaligned");
438 volatile int uninit_src;
439 volatile char dst[8] = { 0 };
440
441 kunit_info(
442 test,
443 "memcpy()ing aligned uninit src to unaligned dst (UMR report)\n");
444 memcpy((void *)&dst[1], (void *)&uninit_src, sizeof(uninit_src));
445 kmsan_check_memory((void *)dst, 4);
446 KUNIT_EXPECT_TRUE(test, report_matches(&expect));
447}
448
449/*
450 * Test case: ensure that memcpy() correctly copies uninitialized values between
451 * aligned `src` and unaligned `dst`.
452 *
453 * Copying aligned 4-byte value to an unaligned one leads to touching two
454 * aligned 4-byte values. This test case checks that KMSAN correctly reports an
455 * error on the second of the two values.
456 */
457static void test_memcpy_aligned_to_unaligned2(struct kunit *test)
458{
459 EXPECTATION_UNINIT_VALUE_FN(expect,
460 "test_memcpy_aligned_to_unaligned2");
461 volatile int uninit_src;
462 volatile char dst[8] = { 0 };
463
464 kunit_info(
465 test,
466 "memcpy()ing aligned uninit src to unaligned dst - part 2 (UMR report)\n");
467 memcpy((void *)&dst[1], (void *)&uninit_src, sizeof(uninit_src));
468 kmsan_check_memory((void *)&dst[4], sizeof(uninit_src));
469 KUNIT_EXPECT_TRUE(test, report_matches(&expect));
470}
471
472static noinline void fibonacci(int *array, int size, int start) {
473 if (start < 2 || (start == size))
474 return;
475 array[start] = array[start - 1] + array[start - 2];
476 fibonacci(array, size, start + 1);
477}
478
479static void test_long_origin_chain(struct kunit *test)
480{
481 EXPECTATION_UNINIT_VALUE_FN(expect,
482 "test_long_origin_chain");
483 /* (KMSAN_MAX_ORIGIN_DEPTH * 2) recursive calls to fibonacci(). */
484 volatile int accum[KMSAN_MAX_ORIGIN_DEPTH * 2 + 2];
485 int last = ARRAY_SIZE(accum) - 1;
486
487 kunit_info(
488 test,
489 "origin chain exceeding KMSAN_MAX_ORIGIN_DEPTH (UMR report)\n");
490 /*
491 * We do not set accum[1] to 0, so the uninitializedness will be carried
492 * over to accum[2..last].
493 */
494 accum[0] = 1;
495 fibonacci((int *)accum, ARRAY_SIZE(accum), 2);
496 kmsan_check_memory((void *)&accum[last], sizeof(int));
497 KUNIT_EXPECT_TRUE(test, report_matches(&expect));
498}
499
500static struct kunit_case kmsan_test_cases[] = {
501 KUNIT_CASE(test_uninit_kmalloc),
502 KUNIT_CASE(test_init_kmalloc),
503 KUNIT_CASE(test_init_kzalloc),
504 KUNIT_CASE(test_uninit_stack_var),
505 KUNIT_CASE(test_init_stack_var),
506 KUNIT_CASE(test_params),
507 KUNIT_CASE(test_uninit_multiple_params),
508 KUNIT_CASE(test_uninit_kmsan_check_memory),
509 KUNIT_CASE(test_init_kmsan_vmap_vunmap),
510 KUNIT_CASE(test_init_vmalloc),
511 KUNIT_CASE(test_uaf),
512 KUNIT_CASE(test_percpu_propagate),
513 KUNIT_CASE(test_printk),
514 KUNIT_CASE(test_memcpy_aligned_to_aligned),
515 KUNIT_CASE(test_memcpy_aligned_to_unaligned),
516 KUNIT_CASE(test_memcpy_aligned_to_unaligned2),
517 KUNIT_CASE(test_long_origin_chain),
518 {},
519};
520
521/* ===== End test cases ===== */
522
523static int test_init(struct kunit *test)
524{
525 unsigned long flags;
526
527 spin_lock_irqsave(&observed.lock, flags);
528 observed.header[0] = '\0';
529 observed.ignore = false;
530 observed.available = false;
531 spin_unlock_irqrestore(&observed.lock, flags);
532
533 return 0;
534}
535
536static void test_exit(struct kunit *test)
537{
538}
539
540static void register_tracepoints(struct tracepoint *tp, void *ignore)
541{
542 check_trace_callback_type_console(probe_console);
543 if (!strcmp(tp->name, "console"))
544 WARN_ON(tracepoint_probe_register(tp, probe_console, NULL));
545}
546
547static void unregister_tracepoints(struct tracepoint *tp, void *ignore)
548{
549 if (!strcmp(tp->name, "console"))
550 tracepoint_probe_unregister(tp, probe_console, NULL);
551}
552
553static int kmsan_suite_init(struct kunit_suite *suite)
554{
555 /*
556 * Because we want to be able to build the test as a module, we need to
557 * iterate through all known tracepoints, since the static registration
558 * won't work here.
559 */
560 for_each_kernel_tracepoint(register_tracepoints, NULL);
561 return 0;
562}
563
564static void kmsan_suite_exit(struct kunit_suite *suite)
565{
566 for_each_kernel_tracepoint(unregister_tracepoints, NULL);
567 tracepoint_synchronize_unregister();
568}
569
570static struct kunit_suite kmsan_test_suite = {
571 .name = "kmsan",
572 .test_cases = kmsan_test_cases,
573 .init = test_init,
574 .exit = test_exit,
575 .suite_init = kmsan_suite_init,
576 .suite_exit = kmsan_suite_exit,
577};
578kunit_test_suites(&kmsan_test_suite);
579
580MODULE_LICENSE("GPL");
581MODULE_AUTHOR("Alexander Potapenko <glider@google.com>");