1039a1c42SKees Cook // SPDX-License-Identifier: GPL-2.0 2039a1c42SKees Cook /* 3039a1c42SKees Cook * This is for all the tests relating directly to heap memory, including 4039a1c42SKees Cook * page allocation and slab allocations. 5039a1c42SKees Cook */ 6039a1c42SKees Cook #include "lkdtm.h" 7039a1c42SKees Cook #include <linux/slab.h> 89c4f6ebcSKees Cook #include <linux/vmalloc.h> 9039a1c42SKees Cook #include <linux/sched.h> 10039a1c42SKees Cook 11966fede8SKees Cook static struct kmem_cache *double_free_cache; 12966fede8SKees Cook static struct kmem_cache *a_cache; 13966fede8SKees Cook static struct kmem_cache *b_cache; 14966fede8SKees Cook 15039a1c42SKees Cook /* 169c4f6ebcSKees Cook * If there aren't guard pages, it's likely that a consecutive allocation will 179c4f6ebcSKees Cook * let us overflow into the second allocation without overwriting something real. 189c4f6ebcSKees Cook */ 199c4f6ebcSKees Cook void lkdtm_VMALLOC_LINEAR_OVERFLOW(void) 209c4f6ebcSKees Cook { 219c4f6ebcSKees Cook char *one, *two; 229c4f6ebcSKees Cook 239c4f6ebcSKees Cook one = vzalloc(PAGE_SIZE); 249c4f6ebcSKees Cook two = vzalloc(PAGE_SIZE); 259c4f6ebcSKees Cook 269c4f6ebcSKees Cook pr_info("Attempting vmalloc linear overflow ...\n"); 279c4f6ebcSKees Cook memset(one, 0xAA, PAGE_SIZE + 1); 289c4f6ebcSKees Cook 299c4f6ebcSKees Cook vfree(two); 309c4f6ebcSKees Cook vfree(one); 319c4f6ebcSKees Cook } 329c4f6ebcSKees Cook 339c4f6ebcSKees Cook /* 34039a1c42SKees Cook * This tries to stay within the next largest power-of-2 kmalloc cache 35039a1c42SKees Cook * to avoid actually overwriting anything important if it's not detected 36039a1c42SKees Cook * correctly. 37039a1c42SKees Cook */ 389c4f6ebcSKees Cook void lkdtm_SLAB_LINEAR_OVERFLOW(void) 39039a1c42SKees Cook { 40039a1c42SKees Cook size_t len = 1020; 41039a1c42SKees Cook u32 *data = kmalloc(len, GFP_KERNEL); 42039a1c42SKees Cook if (!data) 43039a1c42SKees Cook return; 44039a1c42SKees Cook 459c4f6ebcSKees Cook pr_info("Attempting slab linear overflow ...\n"); 46039a1c42SKees Cook data[1024 / sizeof(u32)] = 0x12345678; 47039a1c42SKees Cook kfree(data); 48039a1c42SKees Cook } 49039a1c42SKees Cook 50039a1c42SKees Cook void lkdtm_WRITE_AFTER_FREE(void) 51039a1c42SKees Cook { 52039a1c42SKees Cook int *base, *again; 53039a1c42SKees Cook size_t len = 1024; 54039a1c42SKees Cook /* 55039a1c42SKees Cook * The slub allocator uses the first word to store the free 56039a1c42SKees Cook * pointer in some configurations. Use the middle of the 57039a1c42SKees Cook * allocation to avoid running into the freelist 58039a1c42SKees Cook */ 59039a1c42SKees Cook size_t offset = (len / sizeof(*base)) / 2; 60039a1c42SKees Cook 61039a1c42SKees Cook base = kmalloc(len, GFP_KERNEL); 62039a1c42SKees Cook if (!base) 63039a1c42SKees Cook return; 64039a1c42SKees Cook pr_info("Allocated memory %p-%p\n", base, &base[offset * 2]); 65039a1c42SKees Cook pr_info("Attempting bad write to freed memory at %p\n", 66039a1c42SKees Cook &base[offset]); 67039a1c42SKees Cook kfree(base); 68039a1c42SKees Cook base[offset] = 0x0abcdef0; 69039a1c42SKees Cook /* Attempt to notice the overwrite. */ 70039a1c42SKees Cook again = kmalloc(len, GFP_KERNEL); 71039a1c42SKees Cook kfree(again); 72039a1c42SKees Cook if (again != base) 73039a1c42SKees Cook pr_info("Hmm, didn't get the same memory range.\n"); 74039a1c42SKees Cook } 75039a1c42SKees Cook 76039a1c42SKees Cook void lkdtm_READ_AFTER_FREE(void) 77039a1c42SKees Cook { 78039a1c42SKees Cook int *base, *val, saw; 79039a1c42SKees Cook size_t len = 1024; 80039a1c42SKees Cook /* 81e12145cfSKees Cook * The slub allocator will use the either the first word or 82e12145cfSKees Cook * the middle of the allocation to store the free pointer, 83e12145cfSKees Cook * depending on configurations. Store in the second word to 84e12145cfSKees Cook * avoid running into the freelist. 85039a1c42SKees Cook */ 86e12145cfSKees Cook size_t offset = sizeof(*base); 87039a1c42SKees Cook 88039a1c42SKees Cook base = kmalloc(len, GFP_KERNEL); 89039a1c42SKees Cook if (!base) { 90039a1c42SKees Cook pr_info("Unable to allocate base memory.\n"); 91039a1c42SKees Cook return; 92039a1c42SKees Cook } 93039a1c42SKees Cook 94039a1c42SKees Cook val = kmalloc(len, GFP_KERNEL); 95039a1c42SKees Cook if (!val) { 96039a1c42SKees Cook pr_info("Unable to allocate val memory.\n"); 97039a1c42SKees Cook kfree(base); 98039a1c42SKees Cook return; 99039a1c42SKees Cook } 100039a1c42SKees Cook 101039a1c42SKees Cook *val = 0x12345678; 102039a1c42SKees Cook base[offset] = *val; 103039a1c42SKees Cook pr_info("Value in memory before free: %x\n", base[offset]); 104039a1c42SKees Cook 105039a1c42SKees Cook kfree(base); 106039a1c42SKees Cook 107039a1c42SKees Cook pr_info("Attempting bad read from freed memory\n"); 108039a1c42SKees Cook saw = base[offset]; 109039a1c42SKees Cook if (saw != *val) { 110039a1c42SKees Cook /* Good! Poisoning happened, so declare a win. */ 111039a1c42SKees Cook pr_info("Memory correctly poisoned (%x)\n", saw); 112*5b777131SKees Cook } else { 113*5b777131SKees Cook pr_err("FAIL: Memory was not poisoned!\n"); 114*5b777131SKees Cook pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free"); 115039a1c42SKees Cook } 116039a1c42SKees Cook 117039a1c42SKees Cook kfree(val); 118039a1c42SKees Cook } 119039a1c42SKees Cook 120039a1c42SKees Cook void lkdtm_WRITE_BUDDY_AFTER_FREE(void) 121039a1c42SKees Cook { 122039a1c42SKees Cook unsigned long p = __get_free_page(GFP_KERNEL); 123039a1c42SKees Cook if (!p) { 124039a1c42SKees Cook pr_info("Unable to allocate free page\n"); 125039a1c42SKees Cook return; 126039a1c42SKees Cook } 127039a1c42SKees Cook 128039a1c42SKees Cook pr_info("Writing to the buddy page before free\n"); 129039a1c42SKees Cook memset((void *)p, 0x3, PAGE_SIZE); 130039a1c42SKees Cook free_page(p); 131039a1c42SKees Cook schedule(); 132039a1c42SKees Cook pr_info("Attempting bad write to the buddy page after free\n"); 133039a1c42SKees Cook memset((void *)p, 0x78, PAGE_SIZE); 134039a1c42SKees Cook /* Attempt to notice the overwrite. */ 135039a1c42SKees Cook p = __get_free_page(GFP_KERNEL); 136039a1c42SKees Cook free_page(p); 137039a1c42SKees Cook schedule(); 138039a1c42SKees Cook } 139039a1c42SKees Cook 140039a1c42SKees Cook void lkdtm_READ_BUDDY_AFTER_FREE(void) 141039a1c42SKees Cook { 142039a1c42SKees Cook unsigned long p = __get_free_page(GFP_KERNEL); 143039a1c42SKees Cook int saw, *val; 144039a1c42SKees Cook int *base; 145039a1c42SKees Cook 146039a1c42SKees Cook if (!p) { 147039a1c42SKees Cook pr_info("Unable to allocate free page\n"); 148039a1c42SKees Cook return; 149039a1c42SKees Cook } 150039a1c42SKees Cook 151039a1c42SKees Cook val = kmalloc(1024, GFP_KERNEL); 152039a1c42SKees Cook if (!val) { 153039a1c42SKees Cook pr_info("Unable to allocate val memory.\n"); 154039a1c42SKees Cook free_page(p); 155039a1c42SKees Cook return; 156039a1c42SKees Cook } 157039a1c42SKees Cook 158039a1c42SKees Cook base = (int *)p; 159039a1c42SKees Cook 160039a1c42SKees Cook *val = 0x12345678; 161039a1c42SKees Cook base[0] = *val; 162039a1c42SKees Cook pr_info("Value in memory before free: %x\n", base[0]); 163039a1c42SKees Cook free_page(p); 164039a1c42SKees Cook pr_info("Attempting to read from freed memory\n"); 165039a1c42SKees Cook saw = base[0]; 166039a1c42SKees Cook if (saw != *val) { 167039a1c42SKees Cook /* Good! Poisoning happened, so declare a win. */ 168039a1c42SKees Cook pr_info("Memory correctly poisoned (%x)\n", saw); 169*5b777131SKees Cook } else { 170*5b777131SKees Cook pr_err("FAIL: Buddy page was not poisoned!\n"); 171*5b777131SKees Cook pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free"); 172039a1c42SKees Cook } 173039a1c42SKees Cook 174039a1c42SKees Cook kfree(val); 175039a1c42SKees Cook } 176966fede8SKees Cook 177966fede8SKees Cook void lkdtm_SLAB_FREE_DOUBLE(void) 178966fede8SKees Cook { 179966fede8SKees Cook int *val; 180966fede8SKees Cook 181966fede8SKees Cook val = kmem_cache_alloc(double_free_cache, GFP_KERNEL); 182966fede8SKees Cook if (!val) { 183966fede8SKees Cook pr_info("Unable to allocate double_free_cache memory.\n"); 184966fede8SKees Cook return; 185966fede8SKees Cook } 186966fede8SKees Cook 187966fede8SKees Cook /* Just make sure we got real memory. */ 188966fede8SKees Cook *val = 0x12345678; 189966fede8SKees Cook pr_info("Attempting double slab free ...\n"); 190966fede8SKees Cook kmem_cache_free(double_free_cache, val); 191966fede8SKees Cook kmem_cache_free(double_free_cache, val); 192966fede8SKees Cook } 193966fede8SKees Cook 194966fede8SKees Cook void lkdtm_SLAB_FREE_CROSS(void) 195966fede8SKees Cook { 196966fede8SKees Cook int *val; 197966fede8SKees Cook 198966fede8SKees Cook val = kmem_cache_alloc(a_cache, GFP_KERNEL); 199966fede8SKees Cook if (!val) { 200966fede8SKees Cook pr_info("Unable to allocate a_cache memory.\n"); 201966fede8SKees Cook return; 202966fede8SKees Cook } 203966fede8SKees Cook 204966fede8SKees Cook /* Just make sure we got real memory. */ 205966fede8SKees Cook *val = 0x12345679; 206966fede8SKees Cook pr_info("Attempting cross-cache slab free ...\n"); 207966fede8SKees Cook kmem_cache_free(b_cache, val); 208966fede8SKees Cook } 209966fede8SKees Cook 210966fede8SKees Cook void lkdtm_SLAB_FREE_PAGE(void) 211966fede8SKees Cook { 212966fede8SKees Cook unsigned long p = __get_free_page(GFP_KERNEL); 213966fede8SKees Cook 214966fede8SKees Cook pr_info("Attempting non-Slab slab free ...\n"); 215966fede8SKees Cook kmem_cache_free(NULL, (void *)p); 216966fede8SKees Cook free_page(p); 217966fede8SKees Cook } 218966fede8SKees Cook 219966fede8SKees Cook /* 220966fede8SKees Cook * We have constructors to keep the caches distinctly separated without 221966fede8SKees Cook * needing to boot with "slab_nomerge". 222966fede8SKees Cook */ 223966fede8SKees Cook static void ctor_double_free(void *region) 224966fede8SKees Cook { } 225966fede8SKees Cook static void ctor_a(void *region) 226966fede8SKees Cook { } 227966fede8SKees Cook static void ctor_b(void *region) 228966fede8SKees Cook { } 229966fede8SKees Cook 230966fede8SKees Cook void __init lkdtm_heap_init(void) 231966fede8SKees Cook { 232966fede8SKees Cook double_free_cache = kmem_cache_create("lkdtm-heap-double_free", 233966fede8SKees Cook 64, 0, 0, ctor_double_free); 234966fede8SKees Cook a_cache = kmem_cache_create("lkdtm-heap-a", 64, 0, 0, ctor_a); 235966fede8SKees Cook b_cache = kmem_cache_create("lkdtm-heap-b", 64, 0, 0, ctor_b); 236966fede8SKees Cook } 237966fede8SKees Cook 238966fede8SKees Cook void __exit lkdtm_heap_exit(void) 239966fede8SKees Cook { 240966fede8SKees Cook kmem_cache_destroy(double_free_cache); 241966fede8SKees Cook kmem_cache_destroy(a_cache); 242966fede8SKees Cook kmem_cache_destroy(b_cache); 243966fede8SKees Cook } 244