18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This is for all the tests relating directly to heap memory, including 48c2ecf20Sopenharmony_ci * page allocation and slab allocations. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include "lkdtm.h" 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci#include <linux/sched.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_cistatic struct kmem_cache *double_free_cache; 118c2ecf20Sopenharmony_cistatic struct kmem_cache *a_cache; 128c2ecf20Sopenharmony_cistatic struct kmem_cache *b_cache; 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * This tries to stay within the next largest power-of-2 kmalloc cache 168c2ecf20Sopenharmony_ci * to avoid actually overwriting anything important if it's not detected 178c2ecf20Sopenharmony_ci * correctly. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_civoid lkdtm_OVERWRITE_ALLOCATION(void) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci size_t len = 1020; 228c2ecf20Sopenharmony_ci u32 *data = kmalloc(len, GFP_KERNEL); 238c2ecf20Sopenharmony_ci if (!data) 248c2ecf20Sopenharmony_ci return; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci data[1024 / sizeof(u32)] = 0x12345678; 278c2ecf20Sopenharmony_ci kfree(data); 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_civoid lkdtm_WRITE_AFTER_FREE(void) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci int *base, *again; 338c2ecf20Sopenharmony_ci size_t len = 1024; 348c2ecf20Sopenharmony_ci /* 358c2ecf20Sopenharmony_ci * The slub allocator uses the first word to store the free 368c2ecf20Sopenharmony_ci * pointer in some configurations. Use the middle of the 378c2ecf20Sopenharmony_ci * allocation to avoid running into the freelist 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci size_t offset = (len / sizeof(*base)) / 2; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci base = kmalloc(len, GFP_KERNEL); 428c2ecf20Sopenharmony_ci if (!base) 438c2ecf20Sopenharmony_ci return; 448c2ecf20Sopenharmony_ci pr_info("Allocated memory %p-%p\n", base, &base[offset * 2]); 458c2ecf20Sopenharmony_ci pr_info("Attempting bad write to freed memory at %p\n", 468c2ecf20Sopenharmony_ci &base[offset]); 478c2ecf20Sopenharmony_ci kfree(base); 488c2ecf20Sopenharmony_ci base[offset] = 0x0abcdef0; 498c2ecf20Sopenharmony_ci /* Attempt to notice the overwrite. */ 508c2ecf20Sopenharmony_ci again = kmalloc(len, GFP_KERNEL); 518c2ecf20Sopenharmony_ci kfree(again); 528c2ecf20Sopenharmony_ci if (again != base) 538c2ecf20Sopenharmony_ci pr_info("Hmm, didn't get the same memory range.\n"); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_civoid lkdtm_READ_AFTER_FREE(void) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci int *base, *val, saw; 598c2ecf20Sopenharmony_ci size_t len = 1024; 608c2ecf20Sopenharmony_ci /* 618c2ecf20Sopenharmony_ci * The slub allocator will use the either the first word or 628c2ecf20Sopenharmony_ci * the middle of the allocation to store the free pointer, 638c2ecf20Sopenharmony_ci * depending on configurations. Store in the second word to 648c2ecf20Sopenharmony_ci * avoid running into the freelist. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci size_t offset = sizeof(*base); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci base = kmalloc(len, GFP_KERNEL); 698c2ecf20Sopenharmony_ci if (!base) { 708c2ecf20Sopenharmony_ci pr_info("Unable to allocate base memory.\n"); 718c2ecf20Sopenharmony_ci return; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci val = kmalloc(len, GFP_KERNEL); 758c2ecf20Sopenharmony_ci if (!val) { 768c2ecf20Sopenharmony_ci pr_info("Unable to allocate val memory.\n"); 778c2ecf20Sopenharmony_ci kfree(base); 788c2ecf20Sopenharmony_ci return; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci *val = 0x12345678; 828c2ecf20Sopenharmony_ci base[offset] = *val; 838c2ecf20Sopenharmony_ci pr_info("Value in memory before free: %x\n", base[offset]); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci kfree(base); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci pr_info("Attempting bad read from freed memory\n"); 888c2ecf20Sopenharmony_ci saw = base[offset]; 898c2ecf20Sopenharmony_ci if (saw != *val) { 908c2ecf20Sopenharmony_ci /* Good! Poisoning happened, so declare a win. */ 918c2ecf20Sopenharmony_ci pr_info("Memory correctly poisoned (%x)\n", saw); 928c2ecf20Sopenharmony_ci BUG(); 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci pr_info("Memory was not poisoned\n"); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci kfree(val); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_civoid lkdtm_WRITE_BUDDY_AFTER_FREE(void) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci unsigned long p = __get_free_page(GFP_KERNEL); 1028c2ecf20Sopenharmony_ci if (!p) { 1038c2ecf20Sopenharmony_ci pr_info("Unable to allocate free page\n"); 1048c2ecf20Sopenharmony_ci return; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci pr_info("Writing to the buddy page before free\n"); 1088c2ecf20Sopenharmony_ci memset((void *)p, 0x3, PAGE_SIZE); 1098c2ecf20Sopenharmony_ci free_page(p); 1108c2ecf20Sopenharmony_ci schedule(); 1118c2ecf20Sopenharmony_ci pr_info("Attempting bad write to the buddy page after free\n"); 1128c2ecf20Sopenharmony_ci memset((void *)p, 0x78, PAGE_SIZE); 1138c2ecf20Sopenharmony_ci /* Attempt to notice the overwrite. */ 1148c2ecf20Sopenharmony_ci p = __get_free_page(GFP_KERNEL); 1158c2ecf20Sopenharmony_ci free_page(p); 1168c2ecf20Sopenharmony_ci schedule(); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_civoid lkdtm_READ_BUDDY_AFTER_FREE(void) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci unsigned long p = __get_free_page(GFP_KERNEL); 1228c2ecf20Sopenharmony_ci int saw, *val; 1238c2ecf20Sopenharmony_ci int *base; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (!p) { 1268c2ecf20Sopenharmony_ci pr_info("Unable to allocate free page\n"); 1278c2ecf20Sopenharmony_ci return; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci val = kmalloc(1024, GFP_KERNEL); 1318c2ecf20Sopenharmony_ci if (!val) { 1328c2ecf20Sopenharmony_ci pr_info("Unable to allocate val memory.\n"); 1338c2ecf20Sopenharmony_ci free_page(p); 1348c2ecf20Sopenharmony_ci return; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci base = (int *)p; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci *val = 0x12345678; 1408c2ecf20Sopenharmony_ci base[0] = *val; 1418c2ecf20Sopenharmony_ci pr_info("Value in memory before free: %x\n", base[0]); 1428c2ecf20Sopenharmony_ci free_page(p); 1438c2ecf20Sopenharmony_ci pr_info("Attempting to read from freed memory\n"); 1448c2ecf20Sopenharmony_ci saw = base[0]; 1458c2ecf20Sopenharmony_ci if (saw != *val) { 1468c2ecf20Sopenharmony_ci /* Good! Poisoning happened, so declare a win. */ 1478c2ecf20Sopenharmony_ci pr_info("Memory correctly poisoned (%x)\n", saw); 1488c2ecf20Sopenharmony_ci BUG(); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci pr_info("Buddy page was not poisoned\n"); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci kfree(val); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_civoid lkdtm_SLAB_FREE_DOUBLE(void) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci int *val; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci val = kmem_cache_alloc(double_free_cache, GFP_KERNEL); 1608c2ecf20Sopenharmony_ci if (!val) { 1618c2ecf20Sopenharmony_ci pr_info("Unable to allocate double_free_cache memory.\n"); 1628c2ecf20Sopenharmony_ci return; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Just make sure we got real memory. */ 1668c2ecf20Sopenharmony_ci *val = 0x12345678; 1678c2ecf20Sopenharmony_ci pr_info("Attempting double slab free ...\n"); 1688c2ecf20Sopenharmony_ci kmem_cache_free(double_free_cache, val); 1698c2ecf20Sopenharmony_ci kmem_cache_free(double_free_cache, val); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_civoid lkdtm_SLAB_FREE_CROSS(void) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci int *val; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci val = kmem_cache_alloc(a_cache, GFP_KERNEL); 1778c2ecf20Sopenharmony_ci if (!val) { 1788c2ecf20Sopenharmony_ci pr_info("Unable to allocate a_cache memory.\n"); 1798c2ecf20Sopenharmony_ci return; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Just make sure we got real memory. */ 1838c2ecf20Sopenharmony_ci *val = 0x12345679; 1848c2ecf20Sopenharmony_ci pr_info("Attempting cross-cache slab free ...\n"); 1858c2ecf20Sopenharmony_ci kmem_cache_free(b_cache, val); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_civoid lkdtm_SLAB_FREE_PAGE(void) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci unsigned long p = __get_free_page(GFP_KERNEL); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci pr_info("Attempting non-Slab slab free ...\n"); 1938c2ecf20Sopenharmony_ci kmem_cache_free(NULL, (void *)p); 1948c2ecf20Sopenharmony_ci free_page(p); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* 1988c2ecf20Sopenharmony_ci * We have constructors to keep the caches distinctly separated without 1998c2ecf20Sopenharmony_ci * needing to boot with "slab_nomerge". 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_cistatic void ctor_double_free(void *region) 2028c2ecf20Sopenharmony_ci{ } 2038c2ecf20Sopenharmony_cistatic void ctor_a(void *region) 2048c2ecf20Sopenharmony_ci{ } 2058c2ecf20Sopenharmony_cistatic void ctor_b(void *region) 2068c2ecf20Sopenharmony_ci{ } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_civoid __init lkdtm_heap_init(void) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci double_free_cache = kmem_cache_create("lkdtm-heap-double_free", 2118c2ecf20Sopenharmony_ci 64, 0, 0, ctor_double_free); 2128c2ecf20Sopenharmony_ci a_cache = kmem_cache_create("lkdtm-heap-a", 64, 0, 0, ctor_a); 2138c2ecf20Sopenharmony_ci b_cache = kmem_cache_create("lkdtm-heap-b", 64, 0, 0, ctor_b); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_civoid __exit lkdtm_heap_exit(void) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci kmem_cache_destroy(double_free_cache); 2198c2ecf20Sopenharmony_ci kmem_cache_destroy(a_cache); 2208c2ecf20Sopenharmony_ci kmem_cache_destroy(b_cache); 2218c2ecf20Sopenharmony_ci} 222