162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This is for all the tests relating directly to heap memory, including 462306a36Sopenharmony_ci * page allocation and slab allocations. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "lkdtm.h" 762306a36Sopenharmony_ci#include <linux/slab.h> 862306a36Sopenharmony_ci#include <linux/vmalloc.h> 962306a36Sopenharmony_ci#include <linux/sched.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic struct kmem_cache *double_free_cache; 1262306a36Sopenharmony_cistatic struct kmem_cache *a_cache; 1362306a36Sopenharmony_cistatic struct kmem_cache *b_cache; 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* 1662306a36Sopenharmony_ci * Using volatile here means the compiler cannot ever make assumptions 1762306a36Sopenharmony_ci * about this value. This means compile-time length checks involving 1862306a36Sopenharmony_ci * this variable cannot be performed; only run-time checks. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_cistatic volatile int __offset = 1; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * If there aren't guard pages, it's likely that a consecutive allocation will 2462306a36Sopenharmony_ci * let us overflow into the second allocation without overwriting something real. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * This should always be caught because there is an unconditional unmapped 2762306a36Sopenharmony_ci * page after vmap allocations. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistatic void lkdtm_VMALLOC_LINEAR_OVERFLOW(void) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci char *one, *two; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci one = vzalloc(PAGE_SIZE); 3462306a36Sopenharmony_ci OPTIMIZER_HIDE_VAR(one); 3562306a36Sopenharmony_ci two = vzalloc(PAGE_SIZE); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci pr_info("Attempting vmalloc linear overflow ...\n"); 3862306a36Sopenharmony_ci memset(one, 0xAA, PAGE_SIZE + __offset); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci vfree(two); 4162306a36Sopenharmony_ci vfree(one); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * This tries to stay within the next largest power-of-2 kmalloc cache 4662306a36Sopenharmony_ci * to avoid actually overwriting anything important if it's not detected 4762306a36Sopenharmony_ci * correctly. 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * This should get caught by either memory tagging, KASan, or by using 5062306a36Sopenharmony_ci * CONFIG_SLUB_DEBUG=y and slub_debug=ZF (or CONFIG_SLUB_DEBUG_ON=y). 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistatic void lkdtm_SLAB_LINEAR_OVERFLOW(void) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci size_t len = 1020; 5562306a36Sopenharmony_ci u32 *data = kmalloc(len, GFP_KERNEL); 5662306a36Sopenharmony_ci if (!data) 5762306a36Sopenharmony_ci return; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci pr_info("Attempting slab linear overflow ...\n"); 6062306a36Sopenharmony_ci OPTIMIZER_HIDE_VAR(data); 6162306a36Sopenharmony_ci data[1024 / sizeof(u32)] = 0x12345678; 6262306a36Sopenharmony_ci kfree(data); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void lkdtm_WRITE_AFTER_FREE(void) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci int *base, *again; 6862306a36Sopenharmony_ci size_t len = 1024; 6962306a36Sopenharmony_ci /* 7062306a36Sopenharmony_ci * The slub allocator uses the first word to store the free 7162306a36Sopenharmony_ci * pointer in some configurations. Use the middle of the 7262306a36Sopenharmony_ci * allocation to avoid running into the freelist 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci size_t offset = (len / sizeof(*base)) / 2; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci base = kmalloc(len, GFP_KERNEL); 7762306a36Sopenharmony_ci if (!base) 7862306a36Sopenharmony_ci return; 7962306a36Sopenharmony_ci pr_info("Allocated memory %p-%p\n", base, &base[offset * 2]); 8062306a36Sopenharmony_ci pr_info("Attempting bad write to freed memory at %p\n", 8162306a36Sopenharmony_ci &base[offset]); 8262306a36Sopenharmony_ci kfree(base); 8362306a36Sopenharmony_ci base[offset] = 0x0abcdef0; 8462306a36Sopenharmony_ci /* Attempt to notice the overwrite. */ 8562306a36Sopenharmony_ci again = kmalloc(len, GFP_KERNEL); 8662306a36Sopenharmony_ci kfree(again); 8762306a36Sopenharmony_ci if (again != base) 8862306a36Sopenharmony_ci pr_info("Hmm, didn't get the same memory range.\n"); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void lkdtm_READ_AFTER_FREE(void) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci int *base, *val, saw; 9462306a36Sopenharmony_ci size_t len = 1024; 9562306a36Sopenharmony_ci /* 9662306a36Sopenharmony_ci * The slub allocator will use the either the first word or 9762306a36Sopenharmony_ci * the middle of the allocation to store the free pointer, 9862306a36Sopenharmony_ci * depending on configurations. Store in the second word to 9962306a36Sopenharmony_ci * avoid running into the freelist. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci size_t offset = sizeof(*base); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci base = kmalloc(len, GFP_KERNEL); 10462306a36Sopenharmony_ci if (!base) { 10562306a36Sopenharmony_ci pr_info("Unable to allocate base memory.\n"); 10662306a36Sopenharmony_ci return; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci val = kmalloc(len, GFP_KERNEL); 11062306a36Sopenharmony_ci if (!val) { 11162306a36Sopenharmony_ci pr_info("Unable to allocate val memory.\n"); 11262306a36Sopenharmony_ci kfree(base); 11362306a36Sopenharmony_ci return; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci *val = 0x12345678; 11762306a36Sopenharmony_ci base[offset] = *val; 11862306a36Sopenharmony_ci pr_info("Value in memory before free: %x\n", base[offset]); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci kfree(base); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci pr_info("Attempting bad read from freed memory\n"); 12362306a36Sopenharmony_ci saw = base[offset]; 12462306a36Sopenharmony_ci if (saw != *val) { 12562306a36Sopenharmony_ci /* Good! Poisoning happened, so declare a win. */ 12662306a36Sopenharmony_ci pr_info("Memory correctly poisoned (%x)\n", saw); 12762306a36Sopenharmony_ci } else { 12862306a36Sopenharmony_ci pr_err("FAIL: Memory was not poisoned!\n"); 12962306a36Sopenharmony_ci pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free"); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci kfree(val); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void lkdtm_WRITE_BUDDY_AFTER_FREE(void) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci unsigned long p = __get_free_page(GFP_KERNEL); 13862306a36Sopenharmony_ci if (!p) { 13962306a36Sopenharmony_ci pr_info("Unable to allocate free page\n"); 14062306a36Sopenharmony_ci return; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci pr_info("Writing to the buddy page before free\n"); 14462306a36Sopenharmony_ci memset((void *)p, 0x3, PAGE_SIZE); 14562306a36Sopenharmony_ci free_page(p); 14662306a36Sopenharmony_ci schedule(); 14762306a36Sopenharmony_ci pr_info("Attempting bad write to the buddy page after free\n"); 14862306a36Sopenharmony_ci memset((void *)p, 0x78, PAGE_SIZE); 14962306a36Sopenharmony_ci /* Attempt to notice the overwrite. */ 15062306a36Sopenharmony_ci p = __get_free_page(GFP_KERNEL); 15162306a36Sopenharmony_ci free_page(p); 15262306a36Sopenharmony_ci schedule(); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void lkdtm_READ_BUDDY_AFTER_FREE(void) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci unsigned long p = __get_free_page(GFP_KERNEL); 15862306a36Sopenharmony_ci int saw, *val; 15962306a36Sopenharmony_ci int *base; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (!p) { 16262306a36Sopenharmony_ci pr_info("Unable to allocate free page\n"); 16362306a36Sopenharmony_ci return; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci val = kmalloc(1024, GFP_KERNEL); 16762306a36Sopenharmony_ci if (!val) { 16862306a36Sopenharmony_ci pr_info("Unable to allocate val memory.\n"); 16962306a36Sopenharmony_ci free_page(p); 17062306a36Sopenharmony_ci return; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci base = (int *)p; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci *val = 0x12345678; 17662306a36Sopenharmony_ci base[0] = *val; 17762306a36Sopenharmony_ci pr_info("Value in memory before free: %x\n", base[0]); 17862306a36Sopenharmony_ci free_page(p); 17962306a36Sopenharmony_ci pr_info("Attempting to read from freed memory\n"); 18062306a36Sopenharmony_ci saw = base[0]; 18162306a36Sopenharmony_ci if (saw != *val) { 18262306a36Sopenharmony_ci /* Good! Poisoning happened, so declare a win. */ 18362306a36Sopenharmony_ci pr_info("Memory correctly poisoned (%x)\n", saw); 18462306a36Sopenharmony_ci } else { 18562306a36Sopenharmony_ci pr_err("FAIL: Buddy page was not poisoned!\n"); 18662306a36Sopenharmony_ci pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free"); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci kfree(val); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void lkdtm_SLAB_INIT_ON_ALLOC(void) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci u8 *first; 19562306a36Sopenharmony_ci u8 *val; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci first = kmalloc(512, GFP_KERNEL); 19862306a36Sopenharmony_ci if (!first) { 19962306a36Sopenharmony_ci pr_info("Unable to allocate 512 bytes the first time.\n"); 20062306a36Sopenharmony_ci return; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci memset(first, 0xAB, 512); 20462306a36Sopenharmony_ci kfree(first); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci val = kmalloc(512, GFP_KERNEL); 20762306a36Sopenharmony_ci if (!val) { 20862306a36Sopenharmony_ci pr_info("Unable to allocate 512 bytes the second time.\n"); 20962306a36Sopenharmony_ci return; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci if (val != first) { 21262306a36Sopenharmony_ci pr_warn("Reallocation missed clobbered memory.\n"); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (memchr(val, 0xAB, 512) == NULL) { 21662306a36Sopenharmony_ci pr_info("Memory appears initialized (%x, no earlier values)\n", *val); 21762306a36Sopenharmony_ci } else { 21862306a36Sopenharmony_ci pr_err("FAIL: Slab was not initialized\n"); 21962306a36Sopenharmony_ci pr_expected_config_param(CONFIG_INIT_ON_ALLOC_DEFAULT_ON, "init_on_alloc"); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci kfree(val); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void lkdtm_BUDDY_INIT_ON_ALLOC(void) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci u8 *first; 22762306a36Sopenharmony_ci u8 *val; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci first = (u8 *)__get_free_page(GFP_KERNEL); 23062306a36Sopenharmony_ci if (!first) { 23162306a36Sopenharmony_ci pr_info("Unable to allocate first free page\n"); 23262306a36Sopenharmony_ci return; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci memset(first, 0xAB, PAGE_SIZE); 23662306a36Sopenharmony_ci free_page((unsigned long)first); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci val = (u8 *)__get_free_page(GFP_KERNEL); 23962306a36Sopenharmony_ci if (!val) { 24062306a36Sopenharmony_ci pr_info("Unable to allocate second free page\n"); 24162306a36Sopenharmony_ci return; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (val != first) { 24562306a36Sopenharmony_ci pr_warn("Reallocation missed clobbered memory.\n"); 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (memchr(val, 0xAB, PAGE_SIZE) == NULL) { 24962306a36Sopenharmony_ci pr_info("Memory appears initialized (%x, no earlier values)\n", *val); 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci pr_err("FAIL: Slab was not initialized\n"); 25262306a36Sopenharmony_ci pr_expected_config_param(CONFIG_INIT_ON_ALLOC_DEFAULT_ON, "init_on_alloc"); 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci free_page((unsigned long)val); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void lkdtm_SLAB_FREE_DOUBLE(void) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci int *val; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci val = kmem_cache_alloc(double_free_cache, GFP_KERNEL); 26262306a36Sopenharmony_ci if (!val) { 26362306a36Sopenharmony_ci pr_info("Unable to allocate double_free_cache memory.\n"); 26462306a36Sopenharmony_ci return; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Just make sure we got real memory. */ 26862306a36Sopenharmony_ci *val = 0x12345678; 26962306a36Sopenharmony_ci pr_info("Attempting double slab free ...\n"); 27062306a36Sopenharmony_ci kmem_cache_free(double_free_cache, val); 27162306a36Sopenharmony_ci kmem_cache_free(double_free_cache, val); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic void lkdtm_SLAB_FREE_CROSS(void) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci int *val; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci val = kmem_cache_alloc(a_cache, GFP_KERNEL); 27962306a36Sopenharmony_ci if (!val) { 28062306a36Sopenharmony_ci pr_info("Unable to allocate a_cache memory.\n"); 28162306a36Sopenharmony_ci return; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Just make sure we got real memory. */ 28562306a36Sopenharmony_ci *val = 0x12345679; 28662306a36Sopenharmony_ci pr_info("Attempting cross-cache slab free ...\n"); 28762306a36Sopenharmony_ci kmem_cache_free(b_cache, val); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic void lkdtm_SLAB_FREE_PAGE(void) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci unsigned long p = __get_free_page(GFP_KERNEL); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci pr_info("Attempting non-Slab slab free ...\n"); 29562306a36Sopenharmony_ci kmem_cache_free(NULL, (void *)p); 29662306a36Sopenharmony_ci free_page(p); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/* 30062306a36Sopenharmony_ci * We have constructors to keep the caches distinctly separated without 30162306a36Sopenharmony_ci * needing to boot with "slab_nomerge". 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_cistatic void ctor_double_free(void *region) 30462306a36Sopenharmony_ci{ } 30562306a36Sopenharmony_cistatic void ctor_a(void *region) 30662306a36Sopenharmony_ci{ } 30762306a36Sopenharmony_cistatic void ctor_b(void *region) 30862306a36Sopenharmony_ci{ } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_civoid __init lkdtm_heap_init(void) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci double_free_cache = kmem_cache_create("lkdtm-heap-double_free", 31362306a36Sopenharmony_ci 64, 0, 0, ctor_double_free); 31462306a36Sopenharmony_ci a_cache = kmem_cache_create("lkdtm-heap-a", 64, 0, 0, ctor_a); 31562306a36Sopenharmony_ci b_cache = kmem_cache_create("lkdtm-heap-b", 64, 0, 0, ctor_b); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_civoid __exit lkdtm_heap_exit(void) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci kmem_cache_destroy(double_free_cache); 32162306a36Sopenharmony_ci kmem_cache_destroy(a_cache); 32262306a36Sopenharmony_ci kmem_cache_destroy(b_cache); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic struct crashtype crashtypes[] = { 32662306a36Sopenharmony_ci CRASHTYPE(SLAB_LINEAR_OVERFLOW), 32762306a36Sopenharmony_ci CRASHTYPE(VMALLOC_LINEAR_OVERFLOW), 32862306a36Sopenharmony_ci CRASHTYPE(WRITE_AFTER_FREE), 32962306a36Sopenharmony_ci CRASHTYPE(READ_AFTER_FREE), 33062306a36Sopenharmony_ci CRASHTYPE(WRITE_BUDDY_AFTER_FREE), 33162306a36Sopenharmony_ci CRASHTYPE(READ_BUDDY_AFTER_FREE), 33262306a36Sopenharmony_ci CRASHTYPE(SLAB_INIT_ON_ALLOC), 33362306a36Sopenharmony_ci CRASHTYPE(BUDDY_INIT_ON_ALLOC), 33462306a36Sopenharmony_ci CRASHTYPE(SLAB_FREE_DOUBLE), 33562306a36Sopenharmony_ci CRASHTYPE(SLAB_FREE_CROSS), 33662306a36Sopenharmony_ci CRASHTYPE(SLAB_FREE_PAGE), 33762306a36Sopenharmony_ci}; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistruct crashtype_category heap_crashtypes = { 34062306a36Sopenharmony_ci .crashtypes = crashtypes, 34162306a36Sopenharmony_ci .len = ARRAY_SIZE(crashtypes), 34262306a36Sopenharmony_ci}; 343