Changes kcsan-test module to support checking reports that include compound instrumentation. Since we should not fail the test if this support is unavailable, we have to add a config variable that the test can use to decide what to check for.
Unified · Split
| @@ -27,6 +27,12 @@ · 26 unmodified lines | ||
| 27 | 27 | #include <linux/types.h> |
| 28 | 28 | #include <trace/events/printk.h> |
| 29 | 29 | |
| 30 | +#ifdef CONFIG_CC_HAS_TSAN_COMPOUND_READ_BEFORE_WRITE | |
| 31 | +#define __KCSAN_ACCESS_RW(alt) (KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE) | |
| 32 | +#else | |
| 33 | +#define __KCSAN_ACCESS_RW(alt) (alt) | |
| 34 | +#endif | |
| 35 | + | |
| 30 | 36 | /* Points to current test-case memory access "kernels". */ |
| 31 | 37 | static void (*access_kernels[2])(void); |
| 32 | 38 | |
| @@ -186,20 +192,21 @@ static bool report_matches(const struct expect_report *r) · 153 unmodified lines | ||
| 186 | 192 | |
| 187 | 193 | /* Access 1 & 2 */ |
| 188 | 194 | for (i = 0; i < 2; ++i) { |
| 195 | + const int ty = r->access[i].type; | |
| 189 | 196 | const char *const access_type = |
| 190 | − (r->access[i].type & KCSAN_ACCESS_ASSERT) ? | |
| 191 | − ((r->access[i].type & KCSAN_ACCESS_WRITE) ? | |
| 192 | − "assert no accesses" : | |
| 193 | − "assert no writes") : | |
| 194 | − ((r->access[i].type & KCSAN_ACCESS_WRITE) ? | |
| 195 | − "write" : | |
| 196 | − "read"); | |
| 197 | + (ty & KCSAN_ACCESS_ASSERT) ? | |
| 198 | + ((ty & KCSAN_ACCESS_WRITE) ? | |
| 199 | + "assert no accesses" : | |
| 200 | + "assert no writes") : | |
| 201 | + ((ty & KCSAN_ACCESS_WRITE) ? | |
| 202 | + ((ty & KCSAN_ACCESS_COMPOUND) ? | |
| 203 | + "read-write" : | |
| 204 | + "write") : | |
| 205 | + "read"); | |
| 197 | 206 | const char *const access_type_aux = |
| 198 | − (r->access[i].type & KCSAN_ACCESS_ATOMIC) ? | |
| 199 | − " (marked)" : | |
| 200 | − ((r->access[i].type & KCSAN_ACCESS_SCOPED) ? | |
| 201 | − " (scoped)" : | |
| 202 | − ""); | |
| 207 | + (ty & KCSAN_ACCESS_ATOMIC) ? | |
| 208 | + " (marked)" : | |
| 209 | + ((ty & KCSAN_ACCESS_SCOPED) ? " (scoped)" : ""); | |
| 203 | 210 | |
| 204 | 211 | if (i == 1) { |
| 205 | 212 | /* Access 2 */ |
| @@ -277,6 +284,12 @@ static noinline void test_kernel_write_atomic(void) · 71 unmodified lines | ||
| 277 | 284 | WRITE_ONCE(test_var, READ_ONCE_NOCHECK(test_sink) + 1); |
| 278 | 285 | } |
| 279 | 286 | |
| 287 | +static noinline void test_kernel_atomic_rmw(void) | |
| 288 | +{ | |
| 289 | + /* Use builtin, so we can set up the "bad" atomic/non-atomic scenario. */ | |
| 290 | + __atomic_fetch_add(&test_var, 1, __ATOMIC_RELAXED); | |
| 291 | +} | |
| 292 | + | |
| 280 | 293 | __no_kcsan |
| 281 | 294 | static noinline void test_kernel_write_uninstrumented(void) { test_var++; } |
| 282 | 295 | |
| @@ -439,8 +452,8 @@ static void test_concurrent_races(struct kunit *test) · 156 unmodified lines | ||
| 439 | 452 | const struct expect_report expect = { |
| 440 | 453 | .access = { |
| 441 | 454 | /* NULL will match any address. */ |
| 442 | − { test_kernel_rmw_array, NULL, 0, KCSAN_ACCESS_WRITE }, | |
| 443 | − { test_kernel_rmw_array, NULL, 0, 0 }, | |
| 455 | + { test_kernel_rmw_array, NULL, 0, __KCSAN_ACCESS_RW(KCSAN_ACCESS_WRITE) }, | |
| 456 | + { test_kernel_rmw_array, NULL, 0, __KCSAN_ACCESS_RW(0) }, | |
| 444 | 457 | }, |
| 445 | 458 | }; |
| 446 | 459 | static const struct expect_report never = { |
| @@ -629,6 +642,29 @@ static void test_read_plain_atomic_write(struct kunit *test) · 182 unmodified lines | ||
| 629 | 642 | KUNIT_EXPECT_TRUE(test, match_expect); |
| 630 | 643 | } |
| 631 | 644 | |
| 645 | +/* Test that atomic RMWs generate correct report. */ | |
| 646 | +__no_kcsan | |
| 647 | +static void test_read_plain_atomic_rmw(struct kunit *test) | |
| 648 | +{ | |
| 649 | + const struct expect_report expect = { | |
| 650 | + .access = { | |
| 651 | + { test_kernel_read, &test_var, sizeof(test_var), 0 }, | |
| 652 | + { test_kernel_atomic_rmw, &test_var, sizeof(test_var), | |
| 653 | + KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ATOMIC }, | |
| 654 | + }, | |
| 655 | + }; | |
| 656 | + bool match_expect = false; | |
| 657 | + | |
| 658 | + if (IS_ENABLED(CONFIG_KCSAN_IGNORE_ATOMICS)) | |
| 659 | + return; | |
| 660 | + | |
| 661 | + begin_test_checks(test_kernel_read, test_kernel_atomic_rmw); | |
| 662 | + do { | |
| 663 | + match_expect = report_matches(&expect); | |
| 664 | + } while (!end_test_checks(match_expect)); | |
| 665 | + KUNIT_EXPECT_TRUE(test, match_expect); | |
| 666 | +} | |
| 667 | + | |
| 632 | 668 | /* Zero-sized accesses should never cause data race reports. */ |
| 633 | 669 | __no_kcsan |
| 634 | 670 | static void test_zero_size_access(struct kunit *test) |
| @@ -942,6 +978,7 @@ static struct kunit_case kcsan_test_cases[] = { · 307 unmodified lines | ||
| 942 | 978 | KCSAN_KUNIT_CASE(test_write_write_struct_part), |
| 943 | 979 | KCSAN_KUNIT_CASE(test_read_atomic_write_atomic), |
| 944 | 980 | KCSAN_KUNIT_CASE(test_read_plain_atomic_write), |
| 981 | + KCSAN_KUNIT_CASE(test_read_plain_atomic_rmw), | |
| 945 | 982 | KCSAN_KUNIT_CASE(test_zero_size_access), |
| 946 | 983 | KCSAN_KUNIT_CASE(test_data_race), |
| 947 | 984 | KCSAN_KUNIT_CASE(test_assert_exclusive_writer), |
| @@ -40,6 +40,11 @@ menuconfig KCSAN · 39 unmodified lines | ||
| 40 | 40 | |
| 41 | 41 | if KCSAN |
| 42 | 42 | |
| 43 | +# Compiler capabilities that should not fail the test if they are unavailable. | |
| 44 | +config CC_HAS_TSAN_COMPOUND_READ_BEFORE_WRITE | |
| 45 | + def_bool (CC_IS_CLANG && $(cc-option,-fsanitize=thread -mllvm -tsan-compound-read-before-write=1)) || \ | |
| 46 | + (CC_IS_GCC && $(cc-option,-fsanitize=thread --param tsan-compound-read-before-write=1)) | |
| 47 | + | |
| 43 | 48 | config KCSAN_VERBOSE |
| 44 | 49 | bool "Show verbose reports with more information about system state" |
| 45 | 50 | depends on PROVE_LOCKING |