18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This is for all the tests related to copy_to_user() and copy_from_user() 48c2ecf20Sopenharmony_ci * hardening. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include "lkdtm.h" 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 98c2ecf20Sopenharmony_ci#include <linux/sched/task_stack.h> 108c2ecf20Sopenharmony_ci#include <linux/mman.h> 118c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 128c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * Many of the tests here end up using const sizes, but those would 168c2ecf20Sopenharmony_ci * normally be ignored by hardened usercopy, so force the compiler 178c2ecf20Sopenharmony_ci * into choosing the non-const path to make sure we trigger the 188c2ecf20Sopenharmony_ci * hardened usercopy checks by added "unconst" to all the const copies, 198c2ecf20Sopenharmony_ci * and making sure "cache_size" isn't optimized into a const. 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_cistatic volatile size_t unconst; 228c2ecf20Sopenharmony_cistatic volatile size_t cache_size = 1024; 238c2ecf20Sopenharmony_cistatic struct kmem_cache *whitelist_cache; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic const unsigned char test_text[] = "This is a test.\n"; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * Instead of adding -Wno-return-local-addr, just pass the stack address 298c2ecf20Sopenharmony_ci * through a function to obfuscate it from the compiler. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cistatic noinline unsigned char *trick_compiler(unsigned char *stack) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci return stack + unconst; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic noinline unsigned char *do_usercopy_stack_callee(int value) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci unsigned char buf[128]; 398c2ecf20Sopenharmony_ci int i; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* Exercise stack to avoid everything living in registers. */ 428c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(buf); i++) { 438c2ecf20Sopenharmony_ci buf[i] = value & 0xff; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* 478c2ecf20Sopenharmony_ci * Put the target buffer in the middle of stack allocation 488c2ecf20Sopenharmony_ci * so that we don't step on future stack users regardless 498c2ecf20Sopenharmony_ci * of stack growth direction. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci return trick_compiler(&buf[(128/2)-32]); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic noinline void do_usercopy_stack(bool to_user, bool bad_frame) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci unsigned long user_addr; 578c2ecf20Sopenharmony_ci unsigned char good_stack[32]; 588c2ecf20Sopenharmony_ci unsigned char *bad_stack; 598c2ecf20Sopenharmony_ci int i; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* Exercise stack to avoid everything living in registers. */ 628c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(good_stack); i++) 638c2ecf20Sopenharmony_ci good_stack[i] = test_text[i % sizeof(test_text)]; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* This is a pointer to outside our current stack frame. */ 668c2ecf20Sopenharmony_ci if (bad_frame) { 678c2ecf20Sopenharmony_ci bad_stack = do_usercopy_stack_callee((uintptr_t)&bad_stack); 688c2ecf20Sopenharmony_ci } else { 698c2ecf20Sopenharmony_ci /* Put start address just inside stack. */ 708c2ecf20Sopenharmony_ci bad_stack = task_stack_page(current) + THREAD_SIZE; 718c2ecf20Sopenharmony_ci bad_stack -= sizeof(unsigned long); 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#ifdef ARCH_HAS_CURRENT_STACK_POINTER 758c2ecf20Sopenharmony_ci pr_info("stack : %px\n", (void *)current_stack_pointer); 768c2ecf20Sopenharmony_ci#endif 778c2ecf20Sopenharmony_ci pr_info("good_stack: %px-%px\n", good_stack, good_stack + sizeof(good_stack)); 788c2ecf20Sopenharmony_ci pr_info("bad_stack : %px-%px\n", bad_stack, bad_stack + sizeof(good_stack)); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci user_addr = vm_mmap(NULL, 0, PAGE_SIZE, 818c2ecf20Sopenharmony_ci PROT_READ | PROT_WRITE | PROT_EXEC, 828c2ecf20Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, 0); 838c2ecf20Sopenharmony_ci if (user_addr >= TASK_SIZE) { 848c2ecf20Sopenharmony_ci pr_warn("Failed to allocate user memory\n"); 858c2ecf20Sopenharmony_ci return; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (to_user) { 898c2ecf20Sopenharmony_ci pr_info("attempting good copy_to_user of local stack\n"); 908c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)user_addr, good_stack, 918c2ecf20Sopenharmony_ci unconst + sizeof(good_stack))) { 928c2ecf20Sopenharmony_ci pr_warn("copy_to_user failed unexpectedly?!\n"); 938c2ecf20Sopenharmony_ci goto free_user; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci pr_info("attempting bad copy_to_user of distant stack\n"); 978c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)user_addr, bad_stack, 988c2ecf20Sopenharmony_ci unconst + sizeof(good_stack))) { 998c2ecf20Sopenharmony_ci pr_warn("copy_to_user failed, but lacked Oops\n"); 1008c2ecf20Sopenharmony_ci goto free_user; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci } else { 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * There isn't a safe way to not be protected by usercopy 1058c2ecf20Sopenharmony_ci * if we're going to write to another thread's stack. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci if (!bad_frame) 1088c2ecf20Sopenharmony_ci goto free_user; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci pr_info("attempting good copy_from_user of local stack\n"); 1118c2ecf20Sopenharmony_ci if (copy_from_user(good_stack, (void __user *)user_addr, 1128c2ecf20Sopenharmony_ci unconst + sizeof(good_stack))) { 1138c2ecf20Sopenharmony_ci pr_warn("copy_from_user failed unexpectedly?!\n"); 1148c2ecf20Sopenharmony_ci goto free_user; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci pr_info("attempting bad copy_from_user of distant stack\n"); 1188c2ecf20Sopenharmony_ci if (copy_from_user(bad_stack, (void __user *)user_addr, 1198c2ecf20Sopenharmony_ci unconst + sizeof(good_stack))) { 1208c2ecf20Sopenharmony_ci pr_warn("copy_from_user failed, but lacked Oops\n"); 1218c2ecf20Sopenharmony_ci goto free_user; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cifree_user: 1268c2ecf20Sopenharmony_ci vm_munmap(user_addr, PAGE_SIZE); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* 1308c2ecf20Sopenharmony_ci * This checks for whole-object size validation with hardened usercopy, 1318c2ecf20Sopenharmony_ci * with or without usercopy whitelisting. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_cistatic void do_usercopy_heap_size(bool to_user) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci unsigned long user_addr; 1368c2ecf20Sopenharmony_ci unsigned char *one, *two; 1378c2ecf20Sopenharmony_ci void __user *test_user_addr; 1388c2ecf20Sopenharmony_ci void *test_kern_addr; 1398c2ecf20Sopenharmony_ci size_t size = unconst + 1024; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci one = kmalloc(size, GFP_KERNEL); 1428c2ecf20Sopenharmony_ci two = kmalloc(size, GFP_KERNEL); 1438c2ecf20Sopenharmony_ci if (!one || !two) { 1448c2ecf20Sopenharmony_ci pr_warn("Failed to allocate kernel memory\n"); 1458c2ecf20Sopenharmony_ci goto free_kernel; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci user_addr = vm_mmap(NULL, 0, PAGE_SIZE, 1498c2ecf20Sopenharmony_ci PROT_READ | PROT_WRITE | PROT_EXEC, 1508c2ecf20Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, 0); 1518c2ecf20Sopenharmony_ci if (user_addr >= TASK_SIZE) { 1528c2ecf20Sopenharmony_ci pr_warn("Failed to allocate user memory\n"); 1538c2ecf20Sopenharmony_ci goto free_kernel; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci memset(one, 'A', size); 1578c2ecf20Sopenharmony_ci memset(two, 'B', size); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci test_user_addr = (void __user *)(user_addr + 16); 1608c2ecf20Sopenharmony_ci test_kern_addr = one + 16; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (to_user) { 1638c2ecf20Sopenharmony_ci pr_info("attempting good copy_to_user of correct size\n"); 1648c2ecf20Sopenharmony_ci if (copy_to_user(test_user_addr, test_kern_addr, size / 2)) { 1658c2ecf20Sopenharmony_ci pr_warn("copy_to_user failed unexpectedly?!\n"); 1668c2ecf20Sopenharmony_ci goto free_user; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci pr_info("attempting bad copy_to_user of too large size\n"); 1708c2ecf20Sopenharmony_ci if (copy_to_user(test_user_addr, test_kern_addr, size)) { 1718c2ecf20Sopenharmony_ci pr_warn("copy_to_user failed, but lacked Oops\n"); 1728c2ecf20Sopenharmony_ci goto free_user; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci } else { 1758c2ecf20Sopenharmony_ci pr_info("attempting good copy_from_user of correct size\n"); 1768c2ecf20Sopenharmony_ci if (copy_from_user(test_kern_addr, test_user_addr, size / 2)) { 1778c2ecf20Sopenharmony_ci pr_warn("copy_from_user failed unexpectedly?!\n"); 1788c2ecf20Sopenharmony_ci goto free_user; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci pr_info("attempting bad copy_from_user of too large size\n"); 1828c2ecf20Sopenharmony_ci if (copy_from_user(test_kern_addr, test_user_addr, size)) { 1838c2ecf20Sopenharmony_ci pr_warn("copy_from_user failed, but lacked Oops\n"); 1848c2ecf20Sopenharmony_ci goto free_user; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cifree_user: 1898c2ecf20Sopenharmony_ci vm_munmap(user_addr, PAGE_SIZE); 1908c2ecf20Sopenharmony_cifree_kernel: 1918c2ecf20Sopenharmony_ci kfree(one); 1928c2ecf20Sopenharmony_ci kfree(two); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* 1968c2ecf20Sopenharmony_ci * This checks for the specific whitelist window within an object. If this 1978c2ecf20Sopenharmony_ci * test passes, then do_usercopy_heap_size() tests will pass too. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_cistatic void do_usercopy_heap_whitelist(bool to_user) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci unsigned long user_alloc; 2028c2ecf20Sopenharmony_ci unsigned char *buf = NULL; 2038c2ecf20Sopenharmony_ci unsigned char __user *user_addr; 2048c2ecf20Sopenharmony_ci size_t offset, size; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Make sure cache was prepared. */ 2078c2ecf20Sopenharmony_ci if (!whitelist_cache) { 2088c2ecf20Sopenharmony_ci pr_warn("Failed to allocate kernel cache\n"); 2098c2ecf20Sopenharmony_ci return; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* 2138c2ecf20Sopenharmony_ci * Allocate a buffer with a whitelisted window in the buffer. 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_ci buf = kmem_cache_alloc(whitelist_cache, GFP_KERNEL); 2168c2ecf20Sopenharmony_ci if (!buf) { 2178c2ecf20Sopenharmony_ci pr_warn("Failed to allocate buffer from whitelist cache\n"); 2188c2ecf20Sopenharmony_ci goto free_alloc; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Allocate user memory we'll poke at. */ 2228c2ecf20Sopenharmony_ci user_alloc = vm_mmap(NULL, 0, PAGE_SIZE, 2238c2ecf20Sopenharmony_ci PROT_READ | PROT_WRITE | PROT_EXEC, 2248c2ecf20Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, 0); 2258c2ecf20Sopenharmony_ci if (user_alloc >= TASK_SIZE) { 2268c2ecf20Sopenharmony_ci pr_warn("Failed to allocate user memory\n"); 2278c2ecf20Sopenharmony_ci goto free_alloc; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci user_addr = (void __user *)user_alloc; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci memset(buf, 'B', cache_size); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* Whitelisted window in buffer, from kmem_cache_create_usercopy. */ 2348c2ecf20Sopenharmony_ci offset = (cache_size / 4) + unconst; 2358c2ecf20Sopenharmony_ci size = (cache_size / 16) + unconst; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (to_user) { 2388c2ecf20Sopenharmony_ci pr_info("attempting good copy_to_user inside whitelist\n"); 2398c2ecf20Sopenharmony_ci if (copy_to_user(user_addr, buf + offset, size)) { 2408c2ecf20Sopenharmony_ci pr_warn("copy_to_user failed unexpectedly?!\n"); 2418c2ecf20Sopenharmony_ci goto free_user; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci pr_info("attempting bad copy_to_user outside whitelist\n"); 2458c2ecf20Sopenharmony_ci if (copy_to_user(user_addr, buf + offset - 1, size)) { 2468c2ecf20Sopenharmony_ci pr_warn("copy_to_user failed, but lacked Oops\n"); 2478c2ecf20Sopenharmony_ci goto free_user; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci } else { 2508c2ecf20Sopenharmony_ci pr_info("attempting good copy_from_user inside whitelist\n"); 2518c2ecf20Sopenharmony_ci if (copy_from_user(buf + offset, user_addr, size)) { 2528c2ecf20Sopenharmony_ci pr_warn("copy_from_user failed unexpectedly?!\n"); 2538c2ecf20Sopenharmony_ci goto free_user; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci pr_info("attempting bad copy_from_user outside whitelist\n"); 2578c2ecf20Sopenharmony_ci if (copy_from_user(buf + offset - 1, user_addr, size)) { 2588c2ecf20Sopenharmony_ci pr_warn("copy_from_user failed, but lacked Oops\n"); 2598c2ecf20Sopenharmony_ci goto free_user; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cifree_user: 2648c2ecf20Sopenharmony_ci vm_munmap(user_alloc, PAGE_SIZE); 2658c2ecf20Sopenharmony_cifree_alloc: 2668c2ecf20Sopenharmony_ci if (buf) 2678c2ecf20Sopenharmony_ci kmem_cache_free(whitelist_cache, buf); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* Callable tests. */ 2718c2ecf20Sopenharmony_civoid lkdtm_USERCOPY_HEAP_SIZE_TO(void) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci do_usercopy_heap_size(true); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_civoid lkdtm_USERCOPY_HEAP_SIZE_FROM(void) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci do_usercopy_heap_size(false); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_civoid lkdtm_USERCOPY_HEAP_WHITELIST_TO(void) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci do_usercopy_heap_whitelist(true); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_civoid lkdtm_USERCOPY_HEAP_WHITELIST_FROM(void) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci do_usercopy_heap_whitelist(false); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_civoid lkdtm_USERCOPY_STACK_FRAME_TO(void) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci do_usercopy_stack(true, true); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_civoid lkdtm_USERCOPY_STACK_FRAME_FROM(void) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci do_usercopy_stack(false, true); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_civoid lkdtm_USERCOPY_STACK_BEYOND(void) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci do_usercopy_stack(true, false); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_civoid lkdtm_USERCOPY_KERNEL(void) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci unsigned long user_addr; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci user_addr = vm_mmap(NULL, 0, PAGE_SIZE, 3118c2ecf20Sopenharmony_ci PROT_READ | PROT_WRITE | PROT_EXEC, 3128c2ecf20Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, 0); 3138c2ecf20Sopenharmony_ci if (user_addr >= TASK_SIZE) { 3148c2ecf20Sopenharmony_ci pr_warn("Failed to allocate user memory\n"); 3158c2ecf20Sopenharmony_ci return; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci pr_info("attempting good copy_to_user from kernel rodata: %px\n", 3198c2ecf20Sopenharmony_ci test_text); 3208c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)user_addr, test_text, 3218c2ecf20Sopenharmony_ci unconst + sizeof(test_text))) { 3228c2ecf20Sopenharmony_ci pr_warn("copy_to_user failed unexpectedly?!\n"); 3238c2ecf20Sopenharmony_ci goto free_user; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci pr_info("attempting bad copy_to_user from kernel text: %px\n", 3278c2ecf20Sopenharmony_ci vm_mmap); 3288c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)user_addr, function_nocfi(vm_mmap), 3298c2ecf20Sopenharmony_ci unconst + PAGE_SIZE)) { 3308c2ecf20Sopenharmony_ci pr_warn("copy_to_user failed, but lacked Oops\n"); 3318c2ecf20Sopenharmony_ci goto free_user; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci pr_err("FAIL: survived bad copy_to_user()\n"); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cifree_user: 3368c2ecf20Sopenharmony_ci vm_munmap(user_addr, PAGE_SIZE); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_civoid __init lkdtm_usercopy_init(void) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci /* Prepare cache that lacks SLAB_USERCOPY flag. */ 3428c2ecf20Sopenharmony_ci whitelist_cache = 3438c2ecf20Sopenharmony_ci kmem_cache_create_usercopy("lkdtm-usercopy", cache_size, 3448c2ecf20Sopenharmony_ci 0, 0, 3458c2ecf20Sopenharmony_ci cache_size / 4, 3468c2ecf20Sopenharmony_ci cache_size / 16, 3478c2ecf20Sopenharmony_ci NULL); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_civoid __exit lkdtm_usercopy_exit(void) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci kmem_cache_destroy(whitelist_cache); 3538c2ecf20Sopenharmony_ci} 354