18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Kernel module for testing copy_to/from_user infrastructure. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2013 Google Inc. All Rights Reserved 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: 88c2ecf20Sopenharmony_ci * Kees Cook <keescook@chromium.org> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/mman.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/sched.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 188c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci * Several 32-bit architectures support 64-bit {get,put}_user() calls. 228c2ecf20Sopenharmony_ci * As there doesn't appear to be anything that can safely determine 238c2ecf20Sopenharmony_ci * their capability at compile-time, we just have to opt-out certain archs. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci#if BITS_PER_LONG == 64 || (!(defined(CONFIG_ARM) && !defined(MMU)) && \ 268c2ecf20Sopenharmony_ci !defined(CONFIG_M68K) && \ 278c2ecf20Sopenharmony_ci !defined(CONFIG_MICROBLAZE) && \ 288c2ecf20Sopenharmony_ci !defined(CONFIG_NIOS2) && \ 298c2ecf20Sopenharmony_ci !defined(CONFIG_PPC32) && \ 308c2ecf20Sopenharmony_ci !defined(CONFIG_SUPERH)) 318c2ecf20Sopenharmony_ci# define TEST_U64 328c2ecf20Sopenharmony_ci#endif 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define test(condition, msg, ...) \ 358c2ecf20Sopenharmony_ci({ \ 368c2ecf20Sopenharmony_ci int cond = (condition); \ 378c2ecf20Sopenharmony_ci if (cond) \ 388c2ecf20Sopenharmony_ci pr_warn("[%d] " msg "\n", __LINE__, ##__VA_ARGS__); \ 398c2ecf20Sopenharmony_ci cond; \ 408c2ecf20Sopenharmony_ci}) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic bool is_zeroed(void *from, size_t size) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci return memchr_inv(from, 0x0, size) == NULL; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int test_check_nonzero_user(char *kmem, char __user *umem, size_t size) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci int ret = 0; 508c2ecf20Sopenharmony_ci size_t start, end, i, zero_start, zero_end; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (test(size < 2 * PAGE_SIZE, "buffer too small")) 538c2ecf20Sopenharmony_ci return -EINVAL; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* 568c2ecf20Sopenharmony_ci * We want to cross a page boundary to exercise the code more 578c2ecf20Sopenharmony_ci * effectively. We also don't want to make the size we scan too large, 588c2ecf20Sopenharmony_ci * otherwise the test can take a long time and cause soft lockups. So 598c2ecf20Sopenharmony_ci * scan a 1024 byte region across the page boundary. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci size = 1024; 628c2ecf20Sopenharmony_ci start = PAGE_SIZE - (size / 2); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci kmem += start; 658c2ecf20Sopenharmony_ci umem += start; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci zero_start = size / 4; 688c2ecf20Sopenharmony_ci zero_end = size - zero_start; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* 718c2ecf20Sopenharmony_ci * We conduct a series of check_nonzero_user() tests on a block of 728c2ecf20Sopenharmony_ci * memory with the following byte-pattern (trying every possible 738c2ecf20Sopenharmony_ci * [start,end] pair): 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * [ 00 ff 00 ff ... 00 00 00 00 ... ff 00 ff 00 ] 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * And we verify that check_nonzero_user() acts identically to 788c2ecf20Sopenharmony_ci * memchr_inv(). 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci memset(kmem, 0x0, size); 828c2ecf20Sopenharmony_ci for (i = 1; i < zero_start; i += 2) 838c2ecf20Sopenharmony_ci kmem[i] = 0xff; 848c2ecf20Sopenharmony_ci for (i = zero_end; i < size; i += 2) 858c2ecf20Sopenharmony_ci kmem[i] = 0xff; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci ret |= test(copy_to_user(umem, kmem, size), 888c2ecf20Sopenharmony_ci "legitimate copy_to_user failed"); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci for (start = 0; start <= size; start++) { 918c2ecf20Sopenharmony_ci for (end = start; end <= size; end++) { 928c2ecf20Sopenharmony_ci size_t len = end - start; 938c2ecf20Sopenharmony_ci int retval = check_zeroed_user(umem + start, len); 948c2ecf20Sopenharmony_ci int expected = is_zeroed(kmem + start, len); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci ret |= test(retval != expected, 978c2ecf20Sopenharmony_ci "check_nonzero_user(=%d) != memchr_inv(=%d) mismatch (start=%zu, end=%zu)", 988c2ecf20Sopenharmony_ci retval, expected, start, end); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return ret; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int test_copy_struct_from_user(char *kmem, char __user *umem, 1068c2ecf20Sopenharmony_ci size_t size) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci int ret = 0; 1098c2ecf20Sopenharmony_ci char *umem_src = NULL, *expected = NULL; 1108c2ecf20Sopenharmony_ci size_t ksize, usize; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci umem_src = kmalloc(size, GFP_KERNEL); 1138c2ecf20Sopenharmony_ci ret = test(umem_src == NULL, "kmalloc failed"); 1148c2ecf20Sopenharmony_ci if (ret) 1158c2ecf20Sopenharmony_ci goto out_free; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci expected = kmalloc(size, GFP_KERNEL); 1188c2ecf20Sopenharmony_ci ret = test(expected == NULL, "kmalloc failed"); 1198c2ecf20Sopenharmony_ci if (ret) 1208c2ecf20Sopenharmony_ci goto out_free; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Fill umem with a fixed byte pattern. */ 1238c2ecf20Sopenharmony_ci memset(umem_src, 0x3e, size); 1248c2ecf20Sopenharmony_ci ret |= test(copy_to_user(umem, umem_src, size), 1258c2ecf20Sopenharmony_ci "legitimate copy_to_user failed"); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Check basic case -- (usize == ksize). */ 1288c2ecf20Sopenharmony_ci ksize = size; 1298c2ecf20Sopenharmony_ci usize = size; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci memcpy(expected, umem_src, ksize); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci memset(kmem, 0x0, size); 1348c2ecf20Sopenharmony_ci ret |= test(copy_struct_from_user(kmem, ksize, umem, usize), 1358c2ecf20Sopenharmony_ci "copy_struct_from_user(usize == ksize) failed"); 1368c2ecf20Sopenharmony_ci ret |= test(memcmp(kmem, expected, ksize), 1378c2ecf20Sopenharmony_ci "copy_struct_from_user(usize == ksize) gives unexpected copy"); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Old userspace case -- (usize < ksize). */ 1408c2ecf20Sopenharmony_ci ksize = size; 1418c2ecf20Sopenharmony_ci usize = size / 2; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci memcpy(expected, umem_src, usize); 1448c2ecf20Sopenharmony_ci memset(expected + usize, 0x0, ksize - usize); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci memset(kmem, 0x0, size); 1478c2ecf20Sopenharmony_ci ret |= test(copy_struct_from_user(kmem, ksize, umem, usize), 1488c2ecf20Sopenharmony_ci "copy_struct_from_user(usize < ksize) failed"); 1498c2ecf20Sopenharmony_ci ret |= test(memcmp(kmem, expected, ksize), 1508c2ecf20Sopenharmony_ci "copy_struct_from_user(usize < ksize) gives unexpected copy"); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* New userspace (-E2BIG) case -- (usize > ksize). */ 1538c2ecf20Sopenharmony_ci ksize = size / 2; 1548c2ecf20Sopenharmony_ci usize = size; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci memset(kmem, 0x0, size); 1578c2ecf20Sopenharmony_ci ret |= test(copy_struct_from_user(kmem, ksize, umem, usize) != -E2BIG, 1588c2ecf20Sopenharmony_ci "copy_struct_from_user(usize > ksize) didn't give E2BIG"); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* New userspace (success) case -- (usize > ksize). */ 1618c2ecf20Sopenharmony_ci ksize = size / 2; 1628c2ecf20Sopenharmony_ci usize = size; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci memcpy(expected, umem_src, ksize); 1658c2ecf20Sopenharmony_ci ret |= test(clear_user(umem + ksize, usize - ksize), 1668c2ecf20Sopenharmony_ci "legitimate clear_user failed"); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci memset(kmem, 0x0, size); 1698c2ecf20Sopenharmony_ci ret |= test(copy_struct_from_user(kmem, ksize, umem, usize), 1708c2ecf20Sopenharmony_ci "copy_struct_from_user(usize > ksize) failed"); 1718c2ecf20Sopenharmony_ci ret |= test(memcmp(kmem, expected, ksize), 1728c2ecf20Sopenharmony_ci "copy_struct_from_user(usize > ksize) gives unexpected copy"); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ciout_free: 1758c2ecf20Sopenharmony_ci kfree(expected); 1768c2ecf20Sopenharmony_ci kfree(umem_src); 1778c2ecf20Sopenharmony_ci return ret; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int __init test_user_copy_init(void) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci int ret = 0; 1838c2ecf20Sopenharmony_ci char *kmem; 1848c2ecf20Sopenharmony_ci char __user *usermem; 1858c2ecf20Sopenharmony_ci char *bad_usermem; 1868c2ecf20Sopenharmony_ci unsigned long user_addr; 1878c2ecf20Sopenharmony_ci u8 val_u8; 1888c2ecf20Sopenharmony_ci u16 val_u16; 1898c2ecf20Sopenharmony_ci u32 val_u32; 1908c2ecf20Sopenharmony_ci#ifdef TEST_U64 1918c2ecf20Sopenharmony_ci u64 val_u64; 1928c2ecf20Sopenharmony_ci#endif 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci kmem = kmalloc(PAGE_SIZE * 2, GFP_KERNEL); 1958c2ecf20Sopenharmony_ci if (!kmem) 1968c2ecf20Sopenharmony_ci return -ENOMEM; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci user_addr = vm_mmap(NULL, 0, PAGE_SIZE * 2, 1998c2ecf20Sopenharmony_ci PROT_READ | PROT_WRITE | PROT_EXEC, 2008c2ecf20Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, 0); 2018c2ecf20Sopenharmony_ci if (user_addr >= (unsigned long)(TASK_SIZE)) { 2028c2ecf20Sopenharmony_ci pr_warn("Failed to allocate user memory\n"); 2038c2ecf20Sopenharmony_ci kfree(kmem); 2048c2ecf20Sopenharmony_ci return -ENOMEM; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci usermem = (char __user *)user_addr; 2088c2ecf20Sopenharmony_ci bad_usermem = (char *)user_addr; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* 2118c2ecf20Sopenharmony_ci * Legitimate usage: none of these copies should fail. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci memset(kmem, 0x3a, PAGE_SIZE * 2); 2148c2ecf20Sopenharmony_ci ret |= test(copy_to_user(usermem, kmem, PAGE_SIZE), 2158c2ecf20Sopenharmony_ci "legitimate copy_to_user failed"); 2168c2ecf20Sopenharmony_ci memset(kmem, 0x0, PAGE_SIZE); 2178c2ecf20Sopenharmony_ci ret |= test(copy_from_user(kmem, usermem, PAGE_SIZE), 2188c2ecf20Sopenharmony_ci "legitimate copy_from_user failed"); 2198c2ecf20Sopenharmony_ci ret |= test(memcmp(kmem, kmem + PAGE_SIZE, PAGE_SIZE), 2208c2ecf20Sopenharmony_ci "legitimate usercopy failed to copy data"); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci#define test_legit(size, check) \ 2238c2ecf20Sopenharmony_ci do { \ 2248c2ecf20Sopenharmony_ci val_##size = check; \ 2258c2ecf20Sopenharmony_ci ret |= test(put_user(val_##size, (size __user *)usermem), \ 2268c2ecf20Sopenharmony_ci "legitimate put_user (" #size ") failed"); \ 2278c2ecf20Sopenharmony_ci val_##size = 0; \ 2288c2ecf20Sopenharmony_ci ret |= test(get_user(val_##size, (size __user *)usermem), \ 2298c2ecf20Sopenharmony_ci "legitimate get_user (" #size ") failed"); \ 2308c2ecf20Sopenharmony_ci ret |= test(val_##size != check, \ 2318c2ecf20Sopenharmony_ci "legitimate get_user (" #size ") failed to do copy"); \ 2328c2ecf20Sopenharmony_ci if (val_##size != check) { \ 2338c2ecf20Sopenharmony_ci pr_info("0x%llx != 0x%llx\n", \ 2348c2ecf20Sopenharmony_ci (unsigned long long)val_##size, \ 2358c2ecf20Sopenharmony_ci (unsigned long long)check); \ 2368c2ecf20Sopenharmony_ci } \ 2378c2ecf20Sopenharmony_ci } while (0) 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci test_legit(u8, 0x5a); 2408c2ecf20Sopenharmony_ci test_legit(u16, 0x5a5b); 2418c2ecf20Sopenharmony_ci test_legit(u32, 0x5a5b5c5d); 2428c2ecf20Sopenharmony_ci#ifdef TEST_U64 2438c2ecf20Sopenharmony_ci test_legit(u64, 0x5a5b5c5d6a6b6c6d); 2448c2ecf20Sopenharmony_ci#endif 2458c2ecf20Sopenharmony_ci#undef test_legit 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Test usage of check_nonzero_user(). */ 2488c2ecf20Sopenharmony_ci ret |= test_check_nonzero_user(kmem, usermem, 2 * PAGE_SIZE); 2498c2ecf20Sopenharmony_ci /* Test usage of copy_struct_from_user(). */ 2508c2ecf20Sopenharmony_ci ret |= test_copy_struct_from_user(kmem, usermem, 2 * PAGE_SIZE); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* 2538c2ecf20Sopenharmony_ci * Invalid usage: none of these copies should succeed. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* Prepare kernel memory with check values. */ 2578c2ecf20Sopenharmony_ci memset(kmem, 0x5a, PAGE_SIZE); 2588c2ecf20Sopenharmony_ci memset(kmem + PAGE_SIZE, 0, PAGE_SIZE); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* Reject kernel-to-kernel copies through copy_from_user(). */ 2618c2ecf20Sopenharmony_ci ret |= test(!copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE), 2628c2ecf20Sopenharmony_ci PAGE_SIZE), 2638c2ecf20Sopenharmony_ci "illegal all-kernel copy_from_user passed"); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* Destination half of buffer should have been zeroed. */ 2668c2ecf20Sopenharmony_ci ret |= test(memcmp(kmem + PAGE_SIZE, kmem, PAGE_SIZE), 2678c2ecf20Sopenharmony_ci "zeroing failure for illegal all-kernel copy_from_user"); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci#if 0 2708c2ecf20Sopenharmony_ci /* 2718c2ecf20Sopenharmony_ci * When running with SMAP/PAN/etc, this will Oops the kernel 2728c2ecf20Sopenharmony_ci * due to the zeroing of userspace memory on failure. This needs 2738c2ecf20Sopenharmony_ci * to be tested in LKDTM instead, since this test module does not 2748c2ecf20Sopenharmony_ci * expect to explode. 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_ci ret |= test(!copy_from_user(bad_usermem, (char __user *)kmem, 2778c2ecf20Sopenharmony_ci PAGE_SIZE), 2788c2ecf20Sopenharmony_ci "illegal reversed copy_from_user passed"); 2798c2ecf20Sopenharmony_ci#endif 2808c2ecf20Sopenharmony_ci ret |= test(!copy_to_user((char __user *)kmem, kmem + PAGE_SIZE, 2818c2ecf20Sopenharmony_ci PAGE_SIZE), 2828c2ecf20Sopenharmony_ci "illegal all-kernel copy_to_user passed"); 2838c2ecf20Sopenharmony_ci ret |= test(!copy_to_user((char __user *)kmem, bad_usermem, 2848c2ecf20Sopenharmony_ci PAGE_SIZE), 2858c2ecf20Sopenharmony_ci "illegal reversed copy_to_user passed"); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci#define test_illegal(size, check) \ 2888c2ecf20Sopenharmony_ci do { \ 2898c2ecf20Sopenharmony_ci val_##size = (check); \ 2908c2ecf20Sopenharmony_ci ret |= test(!get_user(val_##size, (size __user *)kmem), \ 2918c2ecf20Sopenharmony_ci "illegal get_user (" #size ") passed"); \ 2928c2ecf20Sopenharmony_ci ret |= test(val_##size != (size)0, \ 2938c2ecf20Sopenharmony_ci "zeroing failure for illegal get_user (" #size ")"); \ 2948c2ecf20Sopenharmony_ci if (val_##size != (size)0) { \ 2958c2ecf20Sopenharmony_ci pr_info("0x%llx != 0\n", \ 2968c2ecf20Sopenharmony_ci (unsigned long long)val_##size); \ 2978c2ecf20Sopenharmony_ci } \ 2988c2ecf20Sopenharmony_ci ret |= test(!put_user(val_##size, (size __user *)kmem), \ 2998c2ecf20Sopenharmony_ci "illegal put_user (" #size ") passed"); \ 3008c2ecf20Sopenharmony_ci } while (0) 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci test_illegal(u8, 0x5a); 3038c2ecf20Sopenharmony_ci test_illegal(u16, 0x5a5b); 3048c2ecf20Sopenharmony_ci test_illegal(u32, 0x5a5b5c5d); 3058c2ecf20Sopenharmony_ci#ifdef TEST_U64 3068c2ecf20Sopenharmony_ci test_illegal(u64, 0x5a5b5c5d6a6b6c6d); 3078c2ecf20Sopenharmony_ci#endif 3088c2ecf20Sopenharmony_ci#undef test_illegal 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci vm_munmap(user_addr, PAGE_SIZE * 2); 3118c2ecf20Sopenharmony_ci kfree(kmem); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (ret == 0) { 3148c2ecf20Sopenharmony_ci pr_info("tests passed.\n"); 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return -EINVAL; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cimodule_init(test_user_copy_init); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic void __exit test_user_copy_exit(void) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci pr_info("unloaded.\n"); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cimodule_exit(test_user_copy_exit); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kees Cook <keescook@chromium.org>"); 3318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 332