Commit 1a2473f0 authored by Andrey Konovalov's avatar Andrey Konovalov Committed by Linus Torvalds

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.comSigned-off-by: default avatarAndrey Konovalov <andreyknvl@google.com>
Signed-off-by: default avatarStephen Rothwell <sfr@canb.auug.org.au>
Acked-by: default avatarMarco 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 8479d7b5
......@@ -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_helpers_tags(struct kunit *test)
{
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)
{
void *area;
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),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment