Commit bec4a247 authored by Marco Elver's avatar Marco Elver Committed by Paul E. McKenney
Browse files

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: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarMarco Elver <elver@google.com>
Signed-off-by: default avatarPaul E. McKenney <paulmck@kernel.org>
parent 9d1335cc
Loading
Loading
Loading
Loading
+51 −14
Original line number Diff line number Diff line
@@ -27,6 +27,12 @@
#include <linux/types.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". */
static void (*access_kernels[2])(void);

@@ -186,20 +192,21 @@ static bool report_matches(const struct expect_report *r)

	/* Access 1 & 2 */
	for (i = 0; i < 2; ++i) {
		const int ty = r->access[i].type;
		const char *const access_type =
			(r->access[i].type & KCSAN_ACCESS_ASSERT) ?
				((r->access[i].type & KCSAN_ACCESS_WRITE) ?
			(ty & KCSAN_ACCESS_ASSERT) ?
				      ((ty & KCSAN_ACCESS_WRITE) ?
					       "assert no accesses" :
					       "assert no writes") :
				((r->access[i].type & KCSAN_ACCESS_WRITE) ?
					 "write" :
				      ((ty & KCSAN_ACCESS_WRITE) ?
					       ((ty & KCSAN_ACCESS_COMPOUND) ?
							"read-write" :
							"write") :
					       "read");
		const char *const access_type_aux =
			(r->access[i].type & KCSAN_ACCESS_ATOMIC) ?
			(ty & KCSAN_ACCESS_ATOMIC) ?
				      " (marked)" :
				((r->access[i].type & KCSAN_ACCESS_SCOPED) ?
					 " (scoped)" :
					 "");
				      ((ty & KCSAN_ACCESS_SCOPED) ? " (scoped)" : "");

		if (i == 1) {
			/* Access 2 */
@@ -277,6 +284,12 @@ static noinline void test_kernel_write_atomic(void)
	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
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 = {
		.access = {
			/* NULL will match any address. */
			{ test_kernel_rmw_array, NULL, 0, KCSAN_ACCESS_WRITE },
			{ test_kernel_rmw_array, NULL, 0, 0 },
			{ test_kernel_rmw_array, NULL, 0, __KCSAN_ACCESS_RW(KCSAN_ACCESS_WRITE) },
			{ test_kernel_rmw_array, NULL, 0, __KCSAN_ACCESS_RW(0) },
		},
	};
	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);
}

/* 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. */
__no_kcsan
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_read_atomic_write_atomic),
	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_data_race),
	KCSAN_KUNIT_CASE(test_assert_exclusive_writer),
+5 −0
Original line number Diff line number Diff line
@@ -40,6 +40,11 @@ menuconfig 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
	bool "Show verbose reports with more information about system state"
	depends on PROVE_LOCKING