xref: /kernel/linux/linux-6.6/kernel/kcsan/selftest.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * KCSAN short boot-time selftests.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2019, Google LLC.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) "kcsan: " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/atomic.h>
1162306a36Sopenharmony_ci#include <linux/bitops.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/kcsan-checks.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/printk.h>
1662306a36Sopenharmony_ci#include <linux/random.h>
1762306a36Sopenharmony_ci#include <linux/sched.h>
1862306a36Sopenharmony_ci#include <linux/spinlock.h>
1962306a36Sopenharmony_ci#include <linux/types.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "encoding.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define ITERS_PER_TEST 2000
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * Test watchpoint encode and decode: check that encoding some access's info,
2762306a36Sopenharmony_ci * and then subsequent decode preserves the access's info.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cistatic bool __init test_encode_decode(void)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	int i;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	for (i = 0; i < ITERS_PER_TEST; ++i) {
3462306a36Sopenharmony_ci		size_t size = get_random_u32_inclusive(1, MAX_ENCODABLE_SIZE);
3562306a36Sopenharmony_ci		bool is_write = !!get_random_u32_below(2);
3662306a36Sopenharmony_ci		unsigned long verif_masked_addr;
3762306a36Sopenharmony_ci		long encoded_watchpoint;
3862306a36Sopenharmony_ci		bool verif_is_write;
3962306a36Sopenharmony_ci		unsigned long addr;
4062306a36Sopenharmony_ci		size_t verif_size;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci		get_random_bytes(&addr, sizeof(addr));
4362306a36Sopenharmony_ci		if (addr < PAGE_SIZE)
4462306a36Sopenharmony_ci			addr = PAGE_SIZE;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci		if (WARN_ON(!check_encodable(addr, size)))
4762306a36Sopenharmony_ci			return false;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci		encoded_watchpoint = encode_watchpoint(addr, size, is_write);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci		/* Check special watchpoints */
5262306a36Sopenharmony_ci		if (WARN_ON(decode_watchpoint(INVALID_WATCHPOINT, &verif_masked_addr, &verif_size, &verif_is_write)))
5362306a36Sopenharmony_ci			return false;
5462306a36Sopenharmony_ci		if (WARN_ON(decode_watchpoint(CONSUMED_WATCHPOINT, &verif_masked_addr, &verif_size, &verif_is_write)))
5562306a36Sopenharmony_ci			return false;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		/* Check decoding watchpoint returns same data */
5862306a36Sopenharmony_ci		if (WARN_ON(!decode_watchpoint(encoded_watchpoint, &verif_masked_addr, &verif_size, &verif_is_write)))
5962306a36Sopenharmony_ci			return false;
6062306a36Sopenharmony_ci		if (WARN_ON(verif_masked_addr != (addr & WATCHPOINT_ADDR_MASK)))
6162306a36Sopenharmony_ci			goto fail;
6262306a36Sopenharmony_ci		if (WARN_ON(verif_size != size))
6362306a36Sopenharmony_ci			goto fail;
6462306a36Sopenharmony_ci		if (WARN_ON(is_write != verif_is_write))
6562306a36Sopenharmony_ci			goto fail;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		continue;
6862306a36Sopenharmony_cifail:
6962306a36Sopenharmony_ci		pr_err("%s fail: %s %zu bytes @ %lx -> encoded: %lx -> %s %zu bytes @ %lx\n",
7062306a36Sopenharmony_ci		       __func__, is_write ? "write" : "read", size, addr, encoded_watchpoint,
7162306a36Sopenharmony_ci		       verif_is_write ? "write" : "read", verif_size, verif_masked_addr);
7262306a36Sopenharmony_ci		return false;
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return true;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* Test access matching function. */
7962306a36Sopenharmony_cistatic bool __init test_matching_access(void)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	if (WARN_ON(!matching_access(10, 1, 10, 1)))
8262306a36Sopenharmony_ci		return false;
8362306a36Sopenharmony_ci	if (WARN_ON(!matching_access(10, 2, 11, 1)))
8462306a36Sopenharmony_ci		return false;
8562306a36Sopenharmony_ci	if (WARN_ON(!matching_access(10, 1, 9, 2)))
8662306a36Sopenharmony_ci		return false;
8762306a36Sopenharmony_ci	if (WARN_ON(matching_access(10, 1, 11, 1)))
8862306a36Sopenharmony_ci		return false;
8962306a36Sopenharmony_ci	if (WARN_ON(matching_access(9, 1, 10, 1)))
9062306a36Sopenharmony_ci		return false;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/*
9362306a36Sopenharmony_ci	 * An access of size 0 could match another access, as demonstrated here.
9462306a36Sopenharmony_ci	 * Rather than add more comparisons to 'matching_access()', which would
9562306a36Sopenharmony_ci	 * end up in the fast-path for *all* checks, check_access() simply
9662306a36Sopenharmony_ci	 * returns for all accesses of size 0.
9762306a36Sopenharmony_ci	 */
9862306a36Sopenharmony_ci	if (WARN_ON(!matching_access(8, 8, 12, 0)))
9962306a36Sopenharmony_ci		return false;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return true;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/*
10562306a36Sopenharmony_ci * Correct memory barrier instrumentation is critical to avoiding false
10662306a36Sopenharmony_ci * positives: simple test to check at boot certain barriers are always properly
10762306a36Sopenharmony_ci * instrumented. See kcsan_test for a more complete test.
10862306a36Sopenharmony_ci */
10962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(test_spinlock);
11062306a36Sopenharmony_cistatic bool __init test_barrier(void)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci#ifdef CONFIG_KCSAN_WEAK_MEMORY
11362306a36Sopenharmony_ci	struct kcsan_scoped_access *reorder_access = &current->kcsan_ctx.reorder_access;
11462306a36Sopenharmony_ci#else
11562306a36Sopenharmony_ci	struct kcsan_scoped_access *reorder_access = NULL;
11662306a36Sopenharmony_ci#endif
11762306a36Sopenharmony_ci	bool ret = true;
11862306a36Sopenharmony_ci	arch_spinlock_t arch_spinlock = __ARCH_SPIN_LOCK_UNLOCKED;
11962306a36Sopenharmony_ci	atomic_t dummy;
12062306a36Sopenharmony_ci	long test_var;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (!reorder_access || !IS_ENABLED(CONFIG_SMP))
12362306a36Sopenharmony_ci		return true;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci#define __KCSAN_CHECK_BARRIER(access_type, barrier, name)					\
12662306a36Sopenharmony_ci	do {											\
12762306a36Sopenharmony_ci		reorder_access->type = (access_type) | KCSAN_ACCESS_SCOPED;			\
12862306a36Sopenharmony_ci		reorder_access->size = 1;							\
12962306a36Sopenharmony_ci		barrier;									\
13062306a36Sopenharmony_ci		if (reorder_access->size != 0) {						\
13162306a36Sopenharmony_ci			pr_err("improperly instrumented type=(" #access_type "): " name "\n");	\
13262306a36Sopenharmony_ci			ret = false;								\
13362306a36Sopenharmony_ci		}										\
13462306a36Sopenharmony_ci	} while (0)
13562306a36Sopenharmony_ci#define KCSAN_CHECK_READ_BARRIER(b)  __KCSAN_CHECK_BARRIER(0, b, #b)
13662306a36Sopenharmony_ci#define KCSAN_CHECK_WRITE_BARRIER(b) __KCSAN_CHECK_BARRIER(KCSAN_ACCESS_WRITE, b, #b)
13762306a36Sopenharmony_ci#define KCSAN_CHECK_RW_BARRIER(b)    __KCSAN_CHECK_BARRIER(KCSAN_ACCESS_WRITE | KCSAN_ACCESS_COMPOUND, b, #b)
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	kcsan_nestable_atomic_begin(); /* No watchpoints in called functions. */
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(mb());
14262306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(rmb());
14362306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(smp_mb());
14462306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(smp_rmb());
14562306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(dma_rmb());
14662306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(smp_mb__before_atomic());
14762306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(smp_mb__after_atomic());
14862306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(smp_mb__after_spinlock());
14962306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(smp_store_mb(test_var, 0));
15062306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(smp_store_release(&test_var, 0));
15162306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(xchg(&test_var, 0));
15262306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(xchg_release(&test_var, 0));
15362306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(cmpxchg(&test_var, 0,  0));
15462306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(cmpxchg_release(&test_var, 0,  0));
15562306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(atomic_set_release(&dummy, 0));
15662306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(atomic_add_return(1, &dummy));
15762306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(atomic_add_return_release(1, &dummy));
15862306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(atomic_fetch_add(1, &dummy));
15962306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(atomic_fetch_add_release(1, &dummy));
16062306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(test_and_set_bit(0, &test_var));
16162306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(test_and_clear_bit(0, &test_var));
16262306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(test_and_change_bit(0, &test_var));
16362306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(clear_bit_unlock(0, &test_var));
16462306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(__clear_bit_unlock(0, &test_var));
16562306a36Sopenharmony_ci	arch_spin_lock(&arch_spinlock);
16662306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(arch_spin_unlock(&arch_spinlock));
16762306a36Sopenharmony_ci	spin_lock(&test_spinlock);
16862306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(spin_unlock(&test_spinlock));
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(mb());
17162306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(wmb());
17262306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(smp_mb());
17362306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(smp_wmb());
17462306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(dma_wmb());
17562306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(smp_mb__before_atomic());
17662306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(smp_mb__after_atomic());
17762306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(smp_mb__after_spinlock());
17862306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(smp_store_mb(test_var, 0));
17962306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(smp_store_release(&test_var, 0));
18062306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(xchg(&test_var, 0));
18162306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(xchg_release(&test_var, 0));
18262306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(cmpxchg(&test_var, 0,  0));
18362306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(cmpxchg_release(&test_var, 0,  0));
18462306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(atomic_set_release(&dummy, 0));
18562306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(atomic_add_return(1, &dummy));
18662306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(atomic_add_return_release(1, &dummy));
18762306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(atomic_fetch_add(1, &dummy));
18862306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(atomic_fetch_add_release(1, &dummy));
18962306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(test_and_set_bit(0, &test_var));
19062306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(test_and_clear_bit(0, &test_var));
19162306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(test_and_change_bit(0, &test_var));
19262306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(clear_bit_unlock(0, &test_var));
19362306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(__clear_bit_unlock(0, &test_var));
19462306a36Sopenharmony_ci	arch_spin_lock(&arch_spinlock);
19562306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(arch_spin_unlock(&arch_spinlock));
19662306a36Sopenharmony_ci	spin_lock(&test_spinlock);
19762306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(spin_unlock(&test_spinlock));
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(mb());
20062306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(wmb());
20162306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(rmb());
20262306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(smp_mb());
20362306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(smp_wmb());
20462306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(smp_rmb());
20562306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(dma_wmb());
20662306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(dma_rmb());
20762306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(smp_mb__before_atomic());
20862306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(smp_mb__after_atomic());
20962306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(smp_mb__after_spinlock());
21062306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(smp_store_mb(test_var, 0));
21162306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(smp_store_release(&test_var, 0));
21262306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(xchg(&test_var, 0));
21362306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(xchg_release(&test_var, 0));
21462306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(cmpxchg(&test_var, 0,  0));
21562306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(cmpxchg_release(&test_var, 0,  0));
21662306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(atomic_set_release(&dummy, 0));
21762306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(atomic_add_return(1, &dummy));
21862306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(atomic_add_return_release(1, &dummy));
21962306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(atomic_fetch_add(1, &dummy));
22062306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(atomic_fetch_add_release(1, &dummy));
22162306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(test_and_set_bit(0, &test_var));
22262306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(test_and_clear_bit(0, &test_var));
22362306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(test_and_change_bit(0, &test_var));
22462306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(clear_bit_unlock(0, &test_var));
22562306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(__clear_bit_unlock(0, &test_var));
22662306a36Sopenharmony_ci	arch_spin_lock(&arch_spinlock);
22762306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(arch_spin_unlock(&arch_spinlock));
22862306a36Sopenharmony_ci	spin_lock(&test_spinlock);
22962306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(spin_unlock(&test_spinlock));
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci#ifdef clear_bit_unlock_is_negative_byte
23262306a36Sopenharmony_ci	KCSAN_CHECK_RW_BARRIER(clear_bit_unlock_is_negative_byte(0, &test_var));
23362306a36Sopenharmony_ci	KCSAN_CHECK_READ_BARRIER(clear_bit_unlock_is_negative_byte(0, &test_var));
23462306a36Sopenharmony_ci	KCSAN_CHECK_WRITE_BARRIER(clear_bit_unlock_is_negative_byte(0, &test_var));
23562306a36Sopenharmony_ci#endif
23662306a36Sopenharmony_ci	kcsan_nestable_atomic_end();
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return ret;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int __init kcsan_selftest(void)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	int passed = 0;
24462306a36Sopenharmony_ci	int total = 0;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci#define RUN_TEST(do_test)                                                      \
24762306a36Sopenharmony_ci	do {                                                                   \
24862306a36Sopenharmony_ci		++total;                                                       \
24962306a36Sopenharmony_ci		if (do_test())                                                 \
25062306a36Sopenharmony_ci			++passed;                                              \
25162306a36Sopenharmony_ci		else                                                           \
25262306a36Sopenharmony_ci			pr_err("selftest: " #do_test " failed");               \
25362306a36Sopenharmony_ci	} while (0)
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	RUN_TEST(test_encode_decode);
25662306a36Sopenharmony_ci	RUN_TEST(test_matching_access);
25762306a36Sopenharmony_ci	RUN_TEST(test_barrier);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	pr_info("selftest: %d/%d tests passed\n", passed, total);
26062306a36Sopenharmony_ci	if (passed != total)
26162306a36Sopenharmony_ci		panic("selftests failed");
26262306a36Sopenharmony_ci	return 0;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_cipostcore_initcall(kcsan_selftest);
265