Skip to content

Commit

Permalink
kasan: add atomic tests
Browse files Browse the repository at this point in the history
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: Paul Heidekrüger <paul.heidekrueger@tum.de>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=214055
Acked-by: Mark 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: Andrew Morton <akpm@linux-foundation.org>
  • Loading branch information
Paul Heidekrüger authored and Andrew Morton committed Feb 22, 2024
1 parent 09dacb7 commit 4e76c8c
Showing 1 changed file with 79 additions and 0 deletions.
79 changes: 79 additions & 0 deletions mm/kasan/kasan_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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),
Expand Down

0 comments on commit 4e76c8c

Please sign in to comment.