在《KASAN(1)-简单实践》中简单测试验证了一下kasan的可用性,但是可以发现,kasan的很多测试用例都集成到kunit中了,而kunit依赖于uml来执行,虽然运行uml可以通过《Linux在x86上的uml》完成,但是对于实际操作和验证而言,模块还是更加方便的,所以我改造了kunit的kasan测试代码,通过模块加载进行测试验证。本文主要分享一下kasan的测试模块,后面文章会进一步逐一进行验证和分析。
代码最后贴出来
修改
diff --git a/lib/Makefile b/lib/Makefile index 1864a5e89741..63a2ea7e6fc2 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_KASAN_KUNIT_TEST) += test_kasan.o CFLAGS_test_kasan.o += -fno-builtin CFLAGS_test_kasan.o += $(call cc-disable-warning, vla) obj-$(CONFIG_KASAN_MODULE_TEST) += test_kasan_module.o +obj-$(CONFIG_KASAN_MODULE_TEST) += test_kasan_kylin.o CFLAGS_test_kasan_module.o += -fno-builtin obj-$(CONFIG_TEST_UBSAN) += test_ubsan.o CFLAGS_test_ubsan.o += $(call cc-disable-warning, vla)
编译
make modules
编译后生成文件test_kasan_kylin.ko
,insmod时加上test的参数即可,如下
for ((i=0;i<43;i++)) do echo "test case:$i" insmod test_kasan_kylin.ko test=${i} sleep 2 done
本人将kasan的kunit测试用例转换成了模块测试,这样对于我来说会很方便进行测试验证,接下来基于我修改的测试用例,进行逐一的运行分析,做好kasan的基本熟悉工作。
测试代码如下:
/* * * Copyright (c) 2025 . * Author: tangfeng <tangfeng@kylinos.cn> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #define pr_fmt(fmt) "kasan test: %s " fmt, __func__ #include <linux/delay.h> #include <linux/kernel.h> #include <linux/mman.h> #include <linux/mm.h> #include <linux/printk.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/uaccess.h> #include <linux/module.h> #include <linux/kasan.h> #include <linux/vmalloc.h> #define __param(type, name, init, msg) \ static type name = init; \ module_param(name, type, 0444); \ MODULE_PARM_DESC(name, msg) \ __param(int, test, -1, "trigger kasan report"); #define KASAN_GRANULE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT) #define OOB_TAG_OFF (IS_ENABLED(CONFIG_KASAN_GENERIC) ? 0 : KASAN_GRANULE_SIZE) #define KASAN_TEST_NEEDS_CONFIG_ON(config) do { \ if (!IS_ENABLED(config)) { \ pr_info("skipping, " #config " required"); \ return; \ } \ } while (0) #define KASAN_TEST_NEEDS_CONFIG_OFF(config) do { \ if (IS_ENABLED(config)) { \ pr_info("skipping, " #config " enabled"); \ return; \ } \ } while (0) void *kasan_ptr_result; int kasan_int_result; static noinline void __init kmalloc_oob_right(void) { char *ptr; size_t size = 123; pr_info("out-of-bounds to right\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } ptr[size + OOB_TAG_OFF] = 'x'; kfree(ptr); } static noinline void __init kmalloc_oob_left(void) { char *ptr; size_t size = 15; pr_info("out-of-bounds to left\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } *ptr = *(ptr - 1); kfree(ptr); } static noinline void __init kmalloc_node_oob_right(void) { char *ptr; size_t size = 4096; pr_info("kmalloc_node(): out-of-bounds to right\n"); ptr = kmalloc_node(size, GFP_KERNEL, 0); if (!ptr) { pr_err("Allocation failed\n"); return; } ptr[size + OOB_TAG_OFF] = 0; kfree(ptr); } static noinline void __init kmalloc_pagealloc_oob_right(void) { char *ptr; size_t size = KMALLOC_MAX_CACHE_SIZE + 10; pr_info("kmalloc pagealloc allocation: out-of-bounds to right\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } ptr[size + OOB_TAG_OFF] = 0; kfree(ptr); } static noinline void __init kmalloc_pagealloc_uaf(void) { char *ptr; size_t size = KMALLOC_MAX_CACHE_SIZE + 10; pr_info("kmalloc pagealloc allocation: use after free\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } kfree(ptr); ptr[0] = 0; } static noinline void __init kmalloc_pagealloc_invalid_free(void) { char *ptr; size_t size = KMALLOC_MAX_CACHE_SIZE + 10; pr_info("kmalloc pagealloc allocation: invalid free\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } kfree(ptr + 1); } static noinline void __init pagealloc_uaf(void) { char *ptr; struct page *pages; size_t order = 4; pr_info("kmalloc pagealloc allocation: use after free\n"); pages = alloc_pages(GFP_KERNEL, order); ptr = page_address(pages); if (!ptr) { pr_err("Allocation failed\n"); return; } free_pages((unsigned long)ptr, order); ptr[0] = 0; } static noinline void __init kmalloc_large_oob_right(void) { char *ptr; size_t size = KMALLOC_MAX_CACHE_SIZE - 256; /* Allocate a chunk that is large enough, but still fits into a slab * and does not trigger the page allocator fallback in SLUB. */ pr_info("kmalloc large allocation: out-of-bounds to right\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } ptr[size + OOB_TAG_OFF] = 0; kfree(ptr); } static noinline void __init krealloc_more_oob_helper(size_t size1, size_t size2) { char *ptr1, *ptr2; size_t middle; middle = size1 + (size2 - size1) / 2; ptr1 = kmalloc(size1, GFP_KERNEL); if (!ptr1) { pr_err("Allocation failed\n"); return; } ptr2 = krealloc(ptr1, size2, GFP_KERNEL); if (!ptr2) { pr_err("Allocation failed\n"); return; } /* All offsets up to size2 must be accessible. */ ptr2[size1 - 1] = 'x'; ptr2[size1] = 'x'; ptr2[middle] = 'x'; ptr2[size2 - 1] = 'x'; /* Generic mode is precise, so unaligned size2 must be inaccessible. */ ptr2[size2] = 'x'; /* For all modes first aligned offset after size2 must be inaccessible. */ ptr2[round_up(size2, KASAN_GRANULE_SIZE)] = 'x'; kfree(ptr2); } static noinline void __init krealloc_less_oob_helper(size_t size1, size_t size2) { char *ptr1, *ptr2; size_t middle; if(size2 > size1) pr_err("size2 > size1\n"); middle = size2 + (size1 - size2) / 2; ptr1 = kmalloc(size1, GFP_KERNEL); if (!ptr1) { pr_err("Allocation failed\n"); return; } ptr2 = krealloc(ptr1, size2, GFP_KERNEL); if (!ptr2) { pr_err("Allocation failed\n"); return; } /* Must be accessible for all modes. */ ptr2[size2 - 1] = 'x'; /* Generic mode is precise, so unaligned size2 must be inaccessible. */ if (IS_ENABLED(CONFIG_KASAN_GENERIC)) ptr2[size2] = 'x'; /* For all modes first aligned offset after size2 must be inaccessible. */ ptr2[round_up(size2, KASAN_GRANULE_SIZE)] = 'x'; /* * For all modes all size2, middle, and size1 should land in separate * granules and thus the latter two offsets should be inaccessible. */ pr_info("Expect size2[%ld] letter than middle[%ld]\n", round_up(size2, KASAN_GRANULE_SIZE), round_down(middle, KASAN_GRANULE_SIZE)); pr_info("Expect middle[%ld] letter than size1[%ld]\n", round_up(middle, KASAN_GRANULE_SIZE), round_down(size1, KASAN_GRANULE_SIZE)); ptr2[middle] = 'x'; ptr2[size1 - 1] = 'x'; ptr2[size1] = 'x'; kfree(ptr2); } static noinline void __init krealloc_more_oob(void) { pr_info("out-of-bounds after krealloc more\n"); krealloc_more_oob_helper(201, 235); } static noinline void __init krealloc_less_oob(void) { pr_info("out-of-bounds after krealloc less\n"); krealloc_less_oob_helper(235, 201); } static noinline void __init krealloc_pagealloc_more_oob(void) { pr_info("out-of-bounds after krealloc pagealloc more\n"); krealloc_more_oob_helper(KMALLOC_MAX_CACHE_SIZE + 201, KMALLOC_MAX_CACHE_SIZE + 235); } static noinline void __init krealloc_pagealloc_less_oob(void) { pr_info("out-of-bounds after krealloc pagealloc less\n"); krealloc_less_oob_helper(KMALLOC_MAX_CACHE_SIZE + 235, KMALLOC_MAX_CACHE_SIZE + 201); } static noinline void __init krealloc_uaf(void) { char *ptr1, *ptr2; int size1 = 201; int size2 = 235; pr_info("krealloc use after free\n"); ptr1 = kmalloc(size1, GFP_KERNEL); if (!ptr1) { pr_err("Allocation failed\n"); return; } kfree(ptr1); ptr2 = krealloc(ptr1, size2, GFP_KERNEL); pr_info("krealloc use after free, assert=%d \n", (void *)ptr2 == NULL); *(volatile char *)ptr1; } static noinline void __init kmalloc_oob_16(void) { struct { u64 words[2]; } *ptr1, *ptr2; pr_info("kmalloc out-of-bounds for 16-bytes access\n"); ptr1 = kmalloc(sizeof(*ptr1) - 3, GFP_KERNEL); ptr2 = kmalloc(sizeof(*ptr2), GFP_KERNEL); if (!ptr1 || !ptr2) { pr_err("Allocation failed\n"); kfree(ptr1); kfree(ptr2); return; } *ptr1 = *ptr2; kfree(ptr1); kfree(ptr2); } static noinline void __init kmalloc_uaf_16(void) { struct { u64 words[2]; } *ptr1, *ptr2; pr_info("kmalloc use-after-free for 16-bytes access\n"); ptr1 = kmalloc(sizeof(*ptr1), GFP_KERNEL); ptr2 = kmalloc(sizeof(*ptr2), GFP_KERNEL); if (!ptr1 || !ptr2) { pr_err("Allocation failed\n"); kfree(ptr1); kfree(ptr2); return; } kfree(ptr2); *ptr1 = *ptr2; kfree(ptr1); } static noinline void __init kmalloc_oob_memset_2(void) { char *ptr; size_t size = 8; pr_info("out-of-bounds in memset2\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } memset(ptr + 7 + OOB_TAG_OFF, 0, 2); kfree(ptr); } static noinline void __init kmalloc_oob_memset_4(void) { char *ptr; size_t size = 8; pr_info("out-of-bounds in memset4\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } memset(ptr + 5 + OOB_TAG_OFF, 0, 4); kfree(ptr); } static noinline void __init kmalloc_oob_memset_8(void) { char *ptr; size_t size = 8; pr_info("out-of-bounds in memset8\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } memset(ptr + 1 + OOB_TAG_OFF, 0, 8); kfree(ptr); } static noinline void __init kmalloc_oob_memset_16(void) { char *ptr; size_t size = 16; pr_info("out-of-bounds in memset16\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } memset(ptr + 1 + OOB_TAG_OFF, 0, 16); kfree(ptr); } static noinline void __init kmalloc_oob_in_memset(void) { char *ptr; size_t size = 666; pr_info("out-of-bounds in memset\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } memset(ptr, 0, size + 5 + OOB_TAG_OFF); kfree(ptr); } static noinline void __init kmalloc_memmove_invalid_size(void) { char *ptr; size_t size = 64; volatile size_t invalid_size = -2; pr_info("memmove invalid size\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } memset((char *)ptr, 0, 64); memmove((char *)ptr, (char *)ptr + 4, invalid_size); kfree(ptr); } static noinline void __init kmalloc_uaf(void) { char *ptr; size_t size = 10; pr_info("use-after-free\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } kfree(ptr); *(ptr + 8) = 'x'; } static noinline void __init kmalloc_uaf_memset(void) { char *ptr; size_t size = 33; pr_info("use-after-free in memset\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } kfree(ptr); memset(ptr, 0, size); } static noinline void __init kmalloc_uaf2(void) { char *ptr1, *ptr2; size_t size = 43; pr_info("use-after-free after another kmalloc\n"); ptr1 = kmalloc(size, GFP_KERNEL); if (!ptr1) { pr_err("Allocation failed\n"); return; } kfree(ptr1); ptr2 = kmalloc(size, GFP_KERNEL); if (!ptr2) { pr_err("Allocation failed\n"); return; } ptr1[40] = 'x'; if (ptr1 == ptr2) pr_err("Could not detect use-after-free: ptr1 == ptr2\n"); kfree(ptr2); } static noinline void __init kfree_via_page(void) { char *ptr; size_t size = 8; struct page *page; unsigned long offset; pr_info("kfree via page\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } page = virt_to_page(ptr); offset = offset_in_page(ptr); kfree(page_address(page) + offset); } static noinline void __init kfree_via_phys(void) { char *ptr; size_t size = 8; phys_addr_t phys; pr_info("kfree via phys\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } phys = virt_to_phys(ptr); kfree(phys_to_virt(phys)); } static noinline void __init kmem_cache_oob(void) { char *p; size_t size = 200; struct kmem_cache *cache = kmem_cache_create("test_cache", size, 0, 0, NULL); if (!cache) { pr_err("Cache allocation failed\n"); return; } pr_info("out-of-bounds in kmem_cache_alloc\n"); p = kmem_cache_alloc(cache, GFP_KERNEL); if (!p) { pr_err("Allocation failed\n"); kmem_cache_destroy(cache); return; } *p = p[size + OOB_TAG_OFF]; kmem_cache_free(cache, p); kmem_cache_destroy(cache); } static noinline void __init kmem_cache_accounted(void) { int i; char *p; size_t size = 200; struct kmem_cache *cache; cache = kmem_cache_create("test_cache", size, 0, SLAB_ACCOUNT, NULL); if (!cache) { pr_err("Cache allocation failed\n"); return; } pr_info("allocate accounted object\n"); /* * Several allocations with a delay to allow for lazy per memcg kmem * cache creation. */ for (i = 0; i < 5; i++) { p = kmem_cache_alloc(cache, GFP_KERNEL); if (!p) { pr_err("Allocation failed\n"); goto free_cache; } kmem_cache_free(cache, p); msleep(100); } free_cache: kmem_cache_destroy(cache); } static noinline void __init kmem_cache_bulk(void) { struct kmem_cache *cache; size_t size = 200; char *p[10]; bool ret; int i; cache = kmem_cache_create("test_cache", size, 0, 0, NULL); if (!cache) { pr_err("Cache allocation failed\n"); return; } ret = kmem_cache_alloc_bulk(cache, GFP_KERNEL, ARRAY_SIZE(p), (void **)&p); if (!ret) { pr_err("Allocation failed: %s\n", __func__); kmem_cache_destroy(cache); return; } pr_info("allocate bulk object\n"); for (i = 0; i < ARRAY_SIZE(p); i++) p[i][0] = p[i][size - 1] = 42; kmem_cache_free_bulk(cache, ARRAY_SIZE(p), (void **)&p); kmem_cache_destroy(cache); } static char global_array[10]; static noinline void __init kasan_global_oob(void) { /* * Deliberate out-of-bounds access. To prevent CONFIG_UBSAN_LOCAL_BOUNDS * from failing here and panicing the kernel, access the array via a * volatile pointer, which will prevent the compiler from being able to * determine the array bounds. * * This access uses a volatile pointer to char (char *volatile) rather * than the more conventional pointer to volatile char (volatile char *) * because we want to prevent the compiler from making inferences about * the pointer itself (i.e. its array bounds), not the data that it * refers to. */ char *volatile array = global_array; char *p = &array[ARRAY_SIZE(global_array) + 3]; /* Only generic mode instruments globals. */ KASAN_TEST_NEEDS_CONFIG_ON(CONFIG_KASAN_GENERIC); pr_info("out-of-bounds global variable\n"); *(volatile char *)p; } static noinline void __init kasan_stack_oob(void) { char stack_array[10]; /* See comment in kasan_global_oob. */ char *volatile array = stack_array; char *p = &array[ARRAY_SIZE(stack_array) + OOB_TAG_OFF]; KASAN_TEST_NEEDS_CONFIG_ON(CONFIG_KASAN_STACK); pr_info("out-of-bounds on stack\n"); *(volatile char *)p; } static noinline void __init ksize_unpoisons_memory(void) { char *ptr; size_t size = 123, real_size; pr_info("ksize() unpoisons the whole allocated chunk\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } real_size = ksize(ptr); /* This access doesn't trigger an error. */ ptr[size] = 'x'; /* This one must. */ ptr[real_size] = 'y'; kfree(ptr); } static noinline void __init ksize_uaf(void) { char *ptr; int size = 128 - KASAN_GRANULE_SIZE; ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } kfree(ptr); ksize(ptr); kasan_int_result = *ptr; kasan_int_result = *(ptr + size); } static noinline void __init kasan_alloca_oob_left(void) { volatile int i = 10; char alloca_array[i]; /* See comment in kasan_global_oob. */ char *volatile array = alloca_array; char *p = array - 1; /* Only generic mode instruments dynamic allocas. */ KASAN_TEST_NEEDS_CONFIG_ON(CONFIG_KASAN_GENERIC); KASAN_TEST_NEEDS_CONFIG_ON(CONFIG_KASAN_STACK); pr_info("out-of-bounds to left on alloca\n"); *(volatile char *)p; } static noinline void __init kasan_alloca_oob_right(void) { volatile int i = 10; char alloca_array[i]; /* See comment in kasan_global_oob. */ char *volatile array = alloca_array; char *p = array + i; /* Only generic mode instruments dynamic allocas. */ KASAN_TEST_NEEDS_CONFIG_ON(CONFIG_KASAN_GENERIC); KASAN_TEST_NEEDS_CONFIG_ON(CONFIG_KASAN_STACK); pr_info("out-of-bounds to right on alloca\n"); *(volatile char *)p; } static noinline void __init kmem_cache_double_free(void) { char *p; size_t size = 200; struct kmem_cache *cache; pr_info("kmem_cache double free\n"); cache = kmem_cache_create("test_cache", size, 0, 0, NULL); if (!cache) { pr_err("Cache allocation failed\n"); return; } p = kmem_cache_alloc(cache, GFP_KERNEL); if (!p) { pr_err("Allocation failed: %s\n", __func__); kmem_cache_destroy(cache); return; } kmem_cache_free(cache, p); /* double free */ kmem_cache_free(cache, p); kmem_cache_destroy(cache); } static noinline void __init kmem_cache_invalid_free(void) { char *p; size_t size = 200; struct kmem_cache *cache; cache = kmem_cache_create("test_cache", size, 0, SLAB_TYPESAFE_BY_RCU, NULL); if (!cache) { pr_err("Cache allocation failed\n"); return; } p = kmem_cache_alloc(cache, GFP_KERNEL); if (!p) { pr_err("Allocation failed: %s\n", __func__); kmem_cache_destroy(cache); return; } pr_info("kmem_cache invalid free\n"); /* Trigger invalid free, the object doesn't get freed. */ kmem_cache_free(cache, p + 1); /* * Properly free the object to prevent the "Objects remaining in * test_cache on __kmem_cache_shutdown" BUG failure. */ kmem_cache_free(cache, p); kmem_cache_destroy(cache); } static noinline void __init kasan_memchr(void) { char *ptr; size_t size = 24; /* * str* functions are not instrumented with CONFIG_AMD_MEM_ENCRYPT. * See https://bugzilla.kernel.org/show_bug.cgi?id=206337 for details. */ KASAN_TEST_NEEDS_CONFIG_OFF(CONFIG_AMD_MEM_ENCRYPT); if (OOB_TAG_OFF) size = round_up(size, OOB_TAG_OFF); ptr = kmalloc(size, GFP_KERNEL | __GFP_ZERO); if (!ptr) { pr_err("Allocation failed\n"); return; } pr_info("memchr out-of-bounds\n"); kasan_ptr_result = memchr(ptr, '1', size + 1); kfree(ptr); } static noinline void __init kasan_memcmp(void) { char *ptr; size_t size = 24; int arr[9]; /* * str* functions are not instrumented with CONFIG_AMD_MEM_ENCRYPT. * See https://bugzilla.kernel.org/show_bug.cgi?id=206337 for details. */ KASAN_TEST_NEEDS_CONFIG_OFF(CONFIG_AMD_MEM_ENCRYPT); if (OOB_TAG_OFF) size = round_up(size, OOB_TAG_OFF); ptr = kmalloc(size, GFP_KERNEL | __GFP_ZERO); if (!ptr) { pr_err("Allocation failed\n"); return; } memset(arr, 0, sizeof(arr)); pr_info("memcmp out-of-bounds\n"); kasan_int_result = memcmp(ptr, arr, size+1); kfree(ptr); } static noinline void __init kasan_strings(void) { char *ptr; size_t size = 24; /* * str* functions are not instrumented with CONFIG_AMD_MEM_ENCRYPT. * See https://bugzilla.kernel.org/show_bug.cgi?id=206337 for details. */ KASAN_TEST_NEEDS_CONFIG_OFF(CONFIG_AMD_MEM_ENCRYPT); ptr = kmalloc(size, GFP_KERNEL | __GFP_ZERO); if (!ptr) { pr_err("Allocation failed\n"); return; } kfree(ptr); pr_info("memcmp use-after-free\n"); /* * Try to cause only 1 invalid access (less spam in dmesg). * For that we need ptr to point to zeroed byte. * Skip metadata that could be stored in freed object so ptr * will likely point to zeroed byte. */ ptr += 16; kasan_ptr_result = strchr(ptr, '1'); kasan_ptr_result = strrchr(ptr, '1'); kasan_int_result = strcmp(ptr, "2"); kasan_int_result = strncmp(ptr, "2", 1); kasan_int_result = strlen(ptr); kasan_int_result = strnlen(ptr, 1); } static noinline void kasan_bitops_modify(int nr, void *addr) { set_bit(nr, addr); __set_bit(nr, addr); clear_bit(nr, addr); __clear_bit(nr, addr); clear_bit_unlock(nr, addr); __clear_bit_unlock(nr, addr); change_bit(nr, addr); __change_bit(nr, addr); } static noinline void kasan_bitops_test_and_modify(int nr, void *addr) { test_and_set_bit(nr, addr); __test_and_set_bit(nr, addr); test_and_set_bit_lock(nr, addr); test_and_clear_bit(nr, addr); __test_and_clear_bit(nr, addr); test_and_change_bit(nr, addr); __test_and_change_bit(nr, addr); kasan_int_result = test_bit(nr, addr); #if defined(clear_bit_unlock_is_negative_byte) kasan_int_result = clear_bit_unlock_is_negative_byte(nr, addr); #endif } static noinline void __init kasan_bitops_generic(void) { long *bits; pr_info("kasan bitops generic\n"); /* This test is specifically crafted for the generic mode. */ KASAN_TEST_NEEDS_CONFIG_ON(CONFIG_KASAN_GENERIC); /* * Allocate 1 more byte, which causes kzalloc to round up to 16 bytes; * this way we do not actually corrupt other memory. */ bits = kzalloc(sizeof(*bits) + 1, GFP_KERNEL); if (!bits) { pr_err("Allocation failed\n"); return; } /* * Below calls try to access bit within allocated memory; however, the * below accesses are still out-of-bounds, since bitops are defined to * operate on the whole long the bit is in. */ kasan_bitops_modify(BITS_PER_LONG, bits); /* * Below calls try to access bit beyond allocated memory. */ kasan_bitops_test_and_modify(BITS_PER_LONG + BITS_PER_BYTE, bits); kfree(bits); } static noinline void __init kmalloc_double_kzfree(void) { char *ptr; size_t size = 16; ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } pr_info("kmalloc double kzfree\n"); kfree_sensitive(ptr); /* double free */ kfree_sensitive(ptr); } static noinline void __init vmalloc_oob(void) { void *area; KASAN_TEST_NEEDS_CONFIG_ON(CONFIG_KASAN_VMALLOC); pr_info("vmalloc out-of-bounds\n"); /* * We have to be careful not to hit the guard page. * The MMU will catch that and crash us. */ area = vmalloc(3000); if (!area) { pr_err("Allocation failed\n"); return; } ((volatile char *)area)[3100]; vfree(area); } static int __init kmalloc_tests_init(void) { bool multishot = kasan_save_enable_multi_shot(); switch (test) { case 0: kmalloc_oob_right(); break; case 1: kmalloc_oob_left(); break; case 2: kmalloc_node_oob_right(); break; case 3: kmalloc_pagealloc_oob_right(); break; case 4: kmalloc_pagealloc_uaf(); break; case 5: kmalloc_pagealloc_invalid_free(); break; case 6: pagealloc_uaf(); break; case 7: kmalloc_large_oob_right(); break; case 8: krealloc_more_oob(); break; case 9: krealloc_less_oob(); break; case 10: krealloc_pagealloc_more_oob(); break; case 11: krealloc_pagealloc_less_oob(); break; case 12: krealloc_uaf(); break; case 13: kmalloc_oob_16(); break; case 14: kmalloc_uaf_16(); break; case 15: kmalloc_oob_in_memset(); break; case 16: kmalloc_oob_memset_2(); break; case 17: kmalloc_oob_memset_4(); break; case 18: kmalloc_oob_memset_8(); break; case 19: kmalloc_oob_memset_16(); break; case 20: kmalloc_memmove_invalid_size(); break; case 21: kmalloc_uaf(); break; case 22: kmalloc_uaf_memset(); break; case 23: kmalloc_uaf2(); break; case 24: kfree_via_page(); break; case 25: kfree_via_phys(); break; case 26: kmem_cache_oob(); break; case 27: kmem_cache_accounted(); break; case 28: kmem_cache_bulk(); break; case 29: kasan_global_oob(); break; case 30: kasan_stack_oob(); break; case 31: kasan_alloca_oob_left(); break; case 32: kasan_alloca_oob_right(); break; case 33: ksize_unpoisons_memory(); break; case 34: ksize_uaf(); break; case 35: kmem_cache_double_free(); break; case 36: kmem_cache_invalid_free(); break; case 37: kasan_memchr(); break; case 38: kasan_memcmp(); break; case 39: kasan_strings(); break; case 40: kasan_bitops_generic(); break; case 41: kmalloc_double_kzfree(); break; case 42: vmalloc_oob(); break; default: break; } kasan_restore_multi_shot(multishot); return -EAGAIN; } module_init(kmalloc_tests_init); MODULE_LICENSE("GPL");