mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	kcsan: Test support for compound instrumentation
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. Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Marco Elver <elver@google.com> Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
This commit is contained in:
		
							parent
							
								
									9d1335cc1e
								
							
						
					
					
						commit
						bec4a24748
					
				
					 2 changed files with 56 additions and 14 deletions
				
			
		| 
						 | 
					@ -27,6 +27,12 @@
 | 
				
			||||||
#include <linux/types.h>
 | 
					#include <linux/types.h>
 | 
				
			||||||
#include <trace/events/printk.h>
 | 
					#include <trace/events/printk.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_CC_HAS_TSAN_COMPOUND_READ_BEFORE_WRITE
 | 
				
			||||||
 | 
					#define __KCSAN_ACCESS_RW(alt) (KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE)
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define __KCSAN_ACCESS_RW(alt) (alt)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Points to current test-case memory access "kernels". */
 | 
					/* Points to current test-case memory access "kernels". */
 | 
				
			||||||
static void (*access_kernels[2])(void);
 | 
					static void (*access_kernels[2])(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -186,20 +192,21 @@ static bool report_matches(const struct expect_report *r)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Access 1 & 2 */
 | 
						/* Access 1 & 2 */
 | 
				
			||||||
	for (i = 0; i < 2; ++i) {
 | 
						for (i = 0; i < 2; ++i) {
 | 
				
			||||||
 | 
							const int ty = r->access[i].type;
 | 
				
			||||||
		const char *const access_type =
 | 
							const char *const access_type =
 | 
				
			||||||
			(r->access[i].type & KCSAN_ACCESS_ASSERT) ?
 | 
								(ty & KCSAN_ACCESS_ASSERT) ?
 | 
				
			||||||
				((r->access[i].type & KCSAN_ACCESS_WRITE) ?
 | 
									      ((ty & KCSAN_ACCESS_WRITE) ?
 | 
				
			||||||
					       "assert no accesses" :
 | 
										       "assert no accesses" :
 | 
				
			||||||
					       "assert no writes") :
 | 
										       "assert no writes") :
 | 
				
			||||||
				((r->access[i].type & KCSAN_ACCESS_WRITE) ?
 | 
									      ((ty & KCSAN_ACCESS_WRITE) ?
 | 
				
			||||||
					 "write" :
 | 
										       ((ty & KCSAN_ACCESS_COMPOUND) ?
 | 
				
			||||||
 | 
												"read-write" :
 | 
				
			||||||
 | 
												"write") :
 | 
				
			||||||
					       "read");
 | 
										       "read");
 | 
				
			||||||
		const char *const access_type_aux =
 | 
							const char *const access_type_aux =
 | 
				
			||||||
			(r->access[i].type & KCSAN_ACCESS_ATOMIC) ?
 | 
								(ty & KCSAN_ACCESS_ATOMIC) ?
 | 
				
			||||||
				      " (marked)" :
 | 
									      " (marked)" :
 | 
				
			||||||
				((r->access[i].type & KCSAN_ACCESS_SCOPED) ?
 | 
									      ((ty & KCSAN_ACCESS_SCOPED) ? " (scoped)" : "");
 | 
				
			||||||
					 " (scoped)" :
 | 
					 | 
				
			||||||
					 "");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (i == 1) {
 | 
							if (i == 1) {
 | 
				
			||||||
			/* Access 2 */
 | 
								/* Access 2 */
 | 
				
			||||||
| 
						 | 
					@ -277,6 +284,12 @@ static noinline void test_kernel_write_atomic(void)
 | 
				
			||||||
	WRITE_ONCE(test_var, READ_ONCE_NOCHECK(test_sink) + 1);
 | 
						WRITE_ONCE(test_var, READ_ONCE_NOCHECK(test_sink) + 1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static noinline void test_kernel_atomic_rmw(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Use builtin, so we can set up the "bad" atomic/non-atomic scenario. */
 | 
				
			||||||
 | 
						__atomic_fetch_add(&test_var, 1, __ATOMIC_RELAXED);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__no_kcsan
 | 
					__no_kcsan
 | 
				
			||||||
static noinline void test_kernel_write_uninstrumented(void) { test_var++; }
 | 
					static noinline void test_kernel_write_uninstrumented(void) { test_var++; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -439,8 +452,8 @@ static void test_concurrent_races(struct kunit *test)
 | 
				
			||||||
	const struct expect_report expect = {
 | 
						const struct expect_report expect = {
 | 
				
			||||||
		.access = {
 | 
							.access = {
 | 
				
			||||||
			/* NULL will match any address. */
 | 
								/* NULL will match any address. */
 | 
				
			||||||
			{ test_kernel_rmw_array, NULL, 0, KCSAN_ACCESS_WRITE },
 | 
								{ test_kernel_rmw_array, NULL, 0, __KCSAN_ACCESS_RW(KCSAN_ACCESS_WRITE) },
 | 
				
			||||||
			{ test_kernel_rmw_array, NULL, 0, 0 },
 | 
								{ test_kernel_rmw_array, NULL, 0, __KCSAN_ACCESS_RW(0) },
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	static const struct expect_report never = {
 | 
						static const struct expect_report never = {
 | 
				
			||||||
| 
						 | 
					@ -629,6 +642,29 @@ static void test_read_plain_atomic_write(struct kunit *test)
 | 
				
			||||||
	KUNIT_EXPECT_TRUE(test, match_expect);
 | 
						KUNIT_EXPECT_TRUE(test, match_expect);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Test that atomic RMWs generate correct report. */
 | 
				
			||||||
 | 
					__no_kcsan
 | 
				
			||||||
 | 
					static void test_read_plain_atomic_rmw(struct kunit *test)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct expect_report expect = {
 | 
				
			||||||
 | 
							.access = {
 | 
				
			||||||
 | 
								{ test_kernel_read, &test_var, sizeof(test_var), 0 },
 | 
				
			||||||
 | 
								{ test_kernel_atomic_rmw, &test_var, sizeof(test_var),
 | 
				
			||||||
 | 
									KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ATOMIC },
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						bool match_expect = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (IS_ENABLED(CONFIG_KCSAN_IGNORE_ATOMICS))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						begin_test_checks(test_kernel_read, test_kernel_atomic_rmw);
 | 
				
			||||||
 | 
						do {
 | 
				
			||||||
 | 
							match_expect = report_matches(&expect);
 | 
				
			||||||
 | 
						} while (!end_test_checks(match_expect));
 | 
				
			||||||
 | 
						KUNIT_EXPECT_TRUE(test, match_expect);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Zero-sized accesses should never cause data race reports. */
 | 
					/* Zero-sized accesses should never cause data race reports. */
 | 
				
			||||||
__no_kcsan
 | 
					__no_kcsan
 | 
				
			||||||
static void test_zero_size_access(struct kunit *test)
 | 
					static void test_zero_size_access(struct kunit *test)
 | 
				
			||||||
| 
						 | 
					@ -942,6 +978,7 @@ static struct kunit_case kcsan_test_cases[] = {
 | 
				
			||||||
	KCSAN_KUNIT_CASE(test_write_write_struct_part),
 | 
						KCSAN_KUNIT_CASE(test_write_write_struct_part),
 | 
				
			||||||
	KCSAN_KUNIT_CASE(test_read_atomic_write_atomic),
 | 
						KCSAN_KUNIT_CASE(test_read_atomic_write_atomic),
 | 
				
			||||||
	KCSAN_KUNIT_CASE(test_read_plain_atomic_write),
 | 
						KCSAN_KUNIT_CASE(test_read_plain_atomic_write),
 | 
				
			||||||
 | 
						KCSAN_KUNIT_CASE(test_read_plain_atomic_rmw),
 | 
				
			||||||
	KCSAN_KUNIT_CASE(test_zero_size_access),
 | 
						KCSAN_KUNIT_CASE(test_zero_size_access),
 | 
				
			||||||
	KCSAN_KUNIT_CASE(test_data_race),
 | 
						KCSAN_KUNIT_CASE(test_data_race),
 | 
				
			||||||
	KCSAN_KUNIT_CASE(test_assert_exclusive_writer),
 | 
						KCSAN_KUNIT_CASE(test_assert_exclusive_writer),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,6 +40,11 @@ menuconfig KCSAN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if KCSAN
 | 
					if KCSAN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Compiler capabilities that should not fail the test if they are unavailable.
 | 
				
			||||||
 | 
					config CC_HAS_TSAN_COMPOUND_READ_BEFORE_WRITE
 | 
				
			||||||
 | 
						def_bool (CC_IS_CLANG && $(cc-option,-fsanitize=thread -mllvm -tsan-compound-read-before-write=1)) || \
 | 
				
			||||||
 | 
							 (CC_IS_GCC && $(cc-option,-fsanitize=thread --param tsan-compound-read-before-write=1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config KCSAN_VERBOSE
 | 
					config KCSAN_VERBOSE
 | 
				
			||||||
	bool "Show verbose reports with more information about system state"
 | 
						bool "Show verbose reports with more information about system state"
 | 
				
			||||||
	depends on PROVE_LOCKING
 | 
						depends on PROVE_LOCKING
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue