|
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) |
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) |
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) |
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) |
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[] = { |
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), |
|
0 commit comments