Commit 4e76c8cc authored by Paul Heidekrüger's avatar Paul Heidekrüger Committed by Andrew Morton
Browse files

kasan: add atomic tests

Test that KASan can detect some unsafe atomic accesses.

As discussed in the linked thread below, these tests attempt to cover
the most common uses of atomics and, therefore, aren't exhaustive.

Link: https://lkml.kernel.org/r/20240202113259.3045705-1-paul.heidekrueger@tum.de
Link: https://lore.kernel.org/all/20240131210041.686657-1-paul.heidekrueger@tum.de/T/#u


Signed-off-by: default avatarPaul Heidekrüger <paul.heidekrueger@tum.de>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=214055


Acked-by: default avatarMark Rutland <mark.rutland@arm.com>
Cc: Marco Elver <elver@google.com>
Cc: Andrey Konovalov <andreyknvl@gmail.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Vincenzo Frascino <vincenzo.frascino@arm.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 09dacb78
Loading
Loading
Loading
Loading
+79 −0
Original line number Diff line number Diff line
@@ -697,6 +697,84 @@ static void kmalloc_uaf3(struct kunit *test)
	KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr1)[8]);
}

static void kasan_atomics_helper(struct kunit *test, void *unsafe, void *safe)
{
	int *i_unsafe = (int *)unsafe;

	KUNIT_EXPECT_KASAN_FAIL(test, READ_ONCE(*i_unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, WRITE_ONCE(*i_unsafe, 42));
	KUNIT_EXPECT_KASAN_FAIL(test, smp_load_acquire(i_unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, smp_store_release(i_unsafe, 42));

	KUNIT_EXPECT_KASAN_FAIL(test, atomic_read(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_set(unsafe, 42));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_add(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_sub(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_inc(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_dec(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_and(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_andnot(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_or(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_xor(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_xchg(unsafe, 42));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_cmpxchg(unsafe, 21, 42));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_try_cmpxchg(unsafe, safe, 42));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_try_cmpxchg(safe, unsafe, 42));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_sub_and_test(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_dec_and_test(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_inc_and_test(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_add_negative(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_add_unless(unsafe, 21, 42));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_inc_not_zero(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_inc_unless_negative(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_dec_unless_positive(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_dec_if_positive(unsafe));

	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_read(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_set(unsafe, 42));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_add(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_sub(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_and(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_andnot(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_or(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_xor(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_xchg(unsafe, 42));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_cmpxchg(unsafe, 21, 42));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_try_cmpxchg(unsafe, safe, 42));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_try_cmpxchg(safe, unsafe, 42));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_sub_and_test(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_and_test(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_and_test(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_add_negative(42, unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_add_unless(unsafe, 21, 42));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_not_zero(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_inc_unless_negative(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_unless_positive(unsafe));
	KUNIT_EXPECT_KASAN_FAIL(test, atomic_long_dec_if_positive(unsafe));
}

static void kasan_atomics(struct kunit *test)
{
	void *a1, *a2;

	/*
	 * Just as with kasan_bitops_tags(), we allocate 48 bytes of memory such
	 * that the following 16 bytes will make up the redzone.
	 */
	a1 = kzalloc(48, GFP_KERNEL);
	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, a1);
	a2 = kzalloc(sizeof(int), GFP_KERNEL);
	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, a1);

	/* Use atomics to access the redzone. */
	kasan_atomics_helper(test, a1 + 48, a2);

	kfree(a1);
	kfree(a2);
}

static void kmalloc_double_kzfree(struct kunit *test)
{
	char *ptr;
@@ -1883,6 +1961,7 @@ static struct kunit_case kasan_kunit_test_cases[] = {
	KUNIT_CASE(kasan_strings),
	KUNIT_CASE(kasan_bitops_generic),
	KUNIT_CASE(kasan_bitops_tags),
	KUNIT_CASE(kasan_atomics),
	KUNIT_CASE(vmalloc_helpers_tags),
	KUNIT_CASE(vmalloc_oob),
	KUNIT_CASE(vmap_tags),