forked from Minki/linux
kasan: improve vmalloc tests
Update the existing vmalloc_oob() test to account for the specifics of the tag-based modes. Also add a few new checks and comments. Add new vmalloc-related tests: - vmalloc_helpers_tags() to check that exported vmalloc helpers can handle tagged pointers. - vmap_tags() to check that SW_TAGS mode properly tags vmap() mappings. - vm_map_ram_tags() to check that SW_TAGS mode properly tags vm_map_ram() mappings. - vmalloc_percpu() to check that SW_TAGS mode tags regions allocated for __alloc_percpu(). The tagging of per-cpu mappings is best-effort; proper tagging is tracked in [1]. [1] https://bugzilla.kernel.org/show_bug.cgi?id=215019 [sfr@canb.auug.org.au: similar to "kasan: test: fix compatibility with FORTIFY_SOURCE"] Link: https://lkml.kernel.org/r/20220128144801.73f5ced0@canb.auug.org.au Link: https://lkml.kernel.org/r/865c91ba49b90623ab50c7526b79ccb955f544f0.1644950160.git.andreyknvl@google.com [andreyknvl@google.com: set_memory_rw/ro() are not exported to modules] Link: https://lkml.kernel.org/r/019ac41602e0c4a7dfe96dc8158a95097c2b2ebd.1645554036.git.andreyknvl@google.com [akpm@linux-foundation.org: fix build] Cc: Andrey Konovalov <andreyknvl@gmail.com> [andreyknvl@google.com: vmap_tags() and vm_map_ram_tags() pass invalid page array size] Link: https://lkml.kernel.org/r/bbdc1c0501c5275e7f26fdb8e2a7b14a40a9f36b.1643047180.git.andreyknvl@google.com Signed-off-by: Andrey Konovalov <andreyknvl@google.com> Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au> Acked-by: Marco Elver <elver@google.com> Cc: Alexander Potapenko <glider@google.com> Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Evgenii Stepanov <eugenis@google.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Peter Collingbourne <pcc@google.com> Cc: Vincenzo Frascino <vincenzo.frascino@arm.com> Cc: Will Deacon <will@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
8479d7b5be
commit
1a2473f0cb
196
lib/test_kasan.c
196
lib/test_kasan.c
@ -19,6 +19,7 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/set_memory.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
|
||||
@ -1057,21 +1058,186 @@ static void kmalloc_double_kzfree(struct kunit *test)
|
||||
KUNIT_EXPECT_KASAN_FAIL(test, kfree_sensitive(ptr));
|
||||
}
|
||||
|
||||
static void vmalloc_oob(struct kunit *test)
|
||||
static void vmalloc_helpers_tags(struct kunit *test)
|
||||
{
|
||||
void *area;
|
||||
void *ptr;
|
||||
|
||||
/* This test is intended for tag-based modes. */
|
||||
KASAN_TEST_NEEDS_CONFIG_OFF(test, CONFIG_KASAN_GENERIC);
|
||||
|
||||
KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_KASAN_VMALLOC);
|
||||
|
||||
ptr = vmalloc(PAGE_SIZE);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
||||
|
||||
/* Check that the returned pointer is tagged. */
|
||||
KUNIT_EXPECT_GE(test, (u8)get_tag(ptr), (u8)KASAN_TAG_MIN);
|
||||
KUNIT_EXPECT_LT(test, (u8)get_tag(ptr), (u8)KASAN_TAG_KERNEL);
|
||||
|
||||
/* Make sure exported vmalloc helpers handle tagged pointers. */
|
||||
KUNIT_ASSERT_TRUE(test, is_vmalloc_addr(ptr));
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vmalloc_to_page(ptr));
|
||||
|
||||
#if !IS_MODULE(CONFIG_KASAN_KUNIT_TEST)
|
||||
{
|
||||
int rv;
|
||||
|
||||
/* Make sure vmalloc'ed memory permissions can be changed. */
|
||||
rv = set_memory_ro((unsigned long)ptr, 1);
|
||||
KUNIT_ASSERT_GE(test, rv, 0);
|
||||
rv = set_memory_rw((unsigned long)ptr, 1);
|
||||
KUNIT_ASSERT_GE(test, rv, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
vfree(ptr);
|
||||
}
|
||||
|
||||
static void vmalloc_oob(struct kunit *test)
|
||||
{
|
||||
char *v_ptr, *p_ptr;
|
||||
struct page *page;
|
||||
size_t size = PAGE_SIZE / 2 - KASAN_GRANULE_SIZE - 5;
|
||||
|
||||
KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_KASAN_VMALLOC);
|
||||
|
||||
v_ptr = vmalloc(size);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, v_ptr);
|
||||
|
||||
OPTIMIZER_HIDE_VAR(v_ptr);
|
||||
|
||||
/*
|
||||
* We have to be careful not to hit the guard page.
|
||||
* We have to be careful not to hit the guard page in vmalloc tests.
|
||||
* The MMU will catch that and crash us.
|
||||
*/
|
||||
area = vmalloc(3000);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, area);
|
||||
|
||||
KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)area)[3100]);
|
||||
vfree(area);
|
||||
/* Make sure in-bounds accesses are valid. */
|
||||
v_ptr[0] = 0;
|
||||
v_ptr[size - 1] = 0;
|
||||
|
||||
/*
|
||||
* An unaligned access past the requested vmalloc size.
|
||||
* Only generic KASAN can precisely detect these.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_KASAN_GENERIC))
|
||||
KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)v_ptr)[size]);
|
||||
|
||||
/* An aligned access into the first out-of-bounds granule. */
|
||||
KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)v_ptr)[size + 5]);
|
||||
|
||||
/* Check that in-bounds accesses to the physical page are valid. */
|
||||
page = vmalloc_to_page(v_ptr);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, page);
|
||||
p_ptr = page_address(page);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p_ptr);
|
||||
p_ptr[0] = 0;
|
||||
|
||||
vfree(v_ptr);
|
||||
|
||||
/*
|
||||
* We can't check for use-after-unmap bugs in this nor in the following
|
||||
* vmalloc tests, as the page might be fully unmapped and accessing it
|
||||
* will crash the kernel.
|
||||
*/
|
||||
}
|
||||
|
||||
static void vmap_tags(struct kunit *test)
|
||||
{
|
||||
char *p_ptr, *v_ptr;
|
||||
struct page *p_page, *v_page;
|
||||
|
||||
/*
|
||||
* This test is specifically crafted for the software tag-based mode,
|
||||
* the only tag-based mode that poisons vmap mappings.
|
||||
*/
|
||||
KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_KASAN_SW_TAGS);
|
||||
|
||||
KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_KASAN_VMALLOC);
|
||||
|
||||
p_page = alloc_pages(GFP_KERNEL, 1);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p_page);
|
||||
p_ptr = page_address(p_page);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p_ptr);
|
||||
|
||||
v_ptr = vmap(&p_page, 1, VM_MAP, PAGE_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, v_ptr);
|
||||
|
||||
/*
|
||||
* We can't check for out-of-bounds bugs in this nor in the following
|
||||
* vmalloc tests, as allocations have page granularity and accessing
|
||||
* the guard page will crash the kernel.
|
||||
*/
|
||||
|
||||
KUNIT_EXPECT_GE(test, (u8)get_tag(v_ptr), (u8)KASAN_TAG_MIN);
|
||||
KUNIT_EXPECT_LT(test, (u8)get_tag(v_ptr), (u8)KASAN_TAG_KERNEL);
|
||||
|
||||
/* Make sure that in-bounds accesses through both pointers work. */
|
||||
*p_ptr = 0;
|
||||
*v_ptr = 0;
|
||||
|
||||
/* Make sure vmalloc_to_page() correctly recovers the page pointer. */
|
||||
v_page = vmalloc_to_page(v_ptr);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, v_page);
|
||||
KUNIT_EXPECT_PTR_EQ(test, p_page, v_page);
|
||||
|
||||
vunmap(v_ptr);
|
||||
free_pages((unsigned long)p_ptr, 1);
|
||||
}
|
||||
|
||||
static void vm_map_ram_tags(struct kunit *test)
|
||||
{
|
||||
char *p_ptr, *v_ptr;
|
||||
struct page *page;
|
||||
|
||||
/*
|
||||
* This test is specifically crafted for the software tag-based mode,
|
||||
* the only tag-based mode that poisons vm_map_ram mappings.
|
||||
*/
|
||||
KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_KASAN_SW_TAGS);
|
||||
|
||||
page = alloc_pages(GFP_KERNEL, 1);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, page);
|
||||
p_ptr = page_address(page);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p_ptr);
|
||||
|
||||
v_ptr = vm_map_ram(&page, 1, -1);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, v_ptr);
|
||||
|
||||
KUNIT_EXPECT_GE(test, (u8)get_tag(v_ptr), (u8)KASAN_TAG_MIN);
|
||||
KUNIT_EXPECT_LT(test, (u8)get_tag(v_ptr), (u8)KASAN_TAG_KERNEL);
|
||||
|
||||
/* Make sure that in-bounds accesses through both pointers work. */
|
||||
*p_ptr = 0;
|
||||
*v_ptr = 0;
|
||||
|
||||
vm_unmap_ram(v_ptr, 1);
|
||||
free_pages((unsigned long)p_ptr, 1);
|
||||
}
|
||||
|
||||
static void vmalloc_percpu(struct kunit *test)
|
||||
{
|
||||
char __percpu *ptr;
|
||||
int cpu;
|
||||
|
||||
/*
|
||||
* This test is specifically crafted for the software tag-based mode,
|
||||
* the only tag-based mode that poisons percpu mappings.
|
||||
*/
|
||||
KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_KASAN_SW_TAGS);
|
||||
|
||||
ptr = __alloc_percpu(PAGE_SIZE, PAGE_SIZE);
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
char *c_ptr = per_cpu_ptr(ptr, cpu);
|
||||
|
||||
KUNIT_EXPECT_GE(test, (u8)get_tag(c_ptr), (u8)KASAN_TAG_MIN);
|
||||
KUNIT_EXPECT_LT(test, (u8)get_tag(c_ptr), (u8)KASAN_TAG_KERNEL);
|
||||
|
||||
/* Make sure that in-bounds accesses don't crash the kernel. */
|
||||
*c_ptr = 0;
|
||||
}
|
||||
|
||||
free_percpu(ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1105,6 +1271,18 @@ static void match_all_not_assigned(struct kunit *test)
|
||||
KUNIT_EXPECT_LT(test, (u8)get_tag(ptr), (u8)KASAN_TAG_KERNEL);
|
||||
free_pages((unsigned long)ptr, order);
|
||||
}
|
||||
|
||||
if (!IS_ENABLED(CONFIG_KASAN_VMALLOC))
|
||||
return;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
size = (get_random_int() % 1024) + 1;
|
||||
ptr = vmalloc(size);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
|
||||
KUNIT_EXPECT_GE(test, (u8)get_tag(ptr), (u8)KASAN_TAG_MIN);
|
||||
KUNIT_EXPECT_LT(test, (u8)get_tag(ptr), (u8)KASAN_TAG_KERNEL);
|
||||
vfree(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that 0xff works as a match-all pointer tag for tag-based modes. */
|
||||
@ -1210,7 +1388,11 @@ static struct kunit_case kasan_kunit_test_cases[] = {
|
||||
KUNIT_CASE(kasan_bitops_generic),
|
||||
KUNIT_CASE(kasan_bitops_tags),
|
||||
KUNIT_CASE(kmalloc_double_kzfree),
|
||||
KUNIT_CASE(vmalloc_helpers_tags),
|
||||
KUNIT_CASE(vmalloc_oob),
|
||||
KUNIT_CASE(vmap_tags),
|
||||
KUNIT_CASE(vm_map_ram_tags),
|
||||
KUNIT_CASE(vmalloc_percpu),
|
||||
KUNIT_CASE(match_all_not_assigned),
|
||||
KUNIT_CASE(match_all_ptr_tag),
|
||||
KUNIT_CASE(match_all_mem_tag),
|
||||
|
Loading…
Reference in New Issue
Block a user