162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This is for all the tests related to validating kernel memory 462306a36Sopenharmony_ci * permissions: non-executable regions, non-writable regions, and 562306a36Sopenharmony_ci * even non-readable regions. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include "lkdtm.h" 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/vmalloc.h> 1062306a36Sopenharmony_ci#include <linux/mman.h> 1162306a36Sopenharmony_ci#include <linux/uaccess.h> 1262306a36Sopenharmony_ci#include <asm/cacheflush.h> 1362306a36Sopenharmony_ci#include <asm/sections.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* Whether or not to fill the target memory area with do_nothing(). */ 1662306a36Sopenharmony_ci#define CODE_WRITE true 1762306a36Sopenharmony_ci#define CODE_AS_IS false 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* How many bytes to copy to be sure we've copied enough of do_nothing(). */ 2062306a36Sopenharmony_ci#define EXEC_SIZE 64 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* This is non-const, so it will end up in the .data section. */ 2362306a36Sopenharmony_cistatic u8 data_area[EXEC_SIZE]; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* This is const, so it will end up in the .rodata section. */ 2662306a36Sopenharmony_cistatic const unsigned long rodata = 0xAA55AA55; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* This is marked __ro_after_init, so it should ultimately be .rodata. */ 2962306a36Sopenharmony_cistatic unsigned long ro_after_init __ro_after_init = 0x55AA5500; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* 3262306a36Sopenharmony_ci * This just returns to the caller. It is designed to be copied into 3362306a36Sopenharmony_ci * non-executable memory regions. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_cistatic noinline void do_nothing(void) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci return; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* Must immediately follow do_nothing for size calculuations to work out. */ 4162306a36Sopenharmony_cistatic noinline void do_overwritten(void) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci pr_info("do_overwritten wasn't overwritten!\n"); 4462306a36Sopenharmony_ci return; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic noinline void do_almost_nothing(void) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci pr_info("do_nothing was hijacked!\n"); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void *setup_function_descriptor(func_desc_t *fdesc, void *dst) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci if (!have_function_descriptors()) 5562306a36Sopenharmony_ci return dst; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci memcpy(fdesc, do_nothing, sizeof(*fdesc)); 5862306a36Sopenharmony_ci fdesc->addr = (unsigned long)dst; 5962306a36Sopenharmony_ci barrier(); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return fdesc; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic noinline void execute_location(void *dst, bool write) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci void (*func)(void); 6762306a36Sopenharmony_ci func_desc_t fdesc; 6862306a36Sopenharmony_ci void *do_nothing_text = dereference_function_descriptor(do_nothing); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci pr_info("attempting ok execution at %px\n", do_nothing_text); 7162306a36Sopenharmony_ci do_nothing(); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (write == CODE_WRITE) { 7462306a36Sopenharmony_ci memcpy(dst, do_nothing_text, EXEC_SIZE); 7562306a36Sopenharmony_ci flush_icache_range((unsigned long)dst, 7662306a36Sopenharmony_ci (unsigned long)dst + EXEC_SIZE); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci pr_info("attempting bad execution at %px\n", dst); 7962306a36Sopenharmony_ci func = setup_function_descriptor(&fdesc, dst); 8062306a36Sopenharmony_ci func(); 8162306a36Sopenharmony_ci pr_err("FAIL: func returned\n"); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic void execute_user_location(void *dst) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci int copied; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* Intentionally crossing kernel/user memory boundary. */ 8962306a36Sopenharmony_ci void (*func)(void); 9062306a36Sopenharmony_ci func_desc_t fdesc; 9162306a36Sopenharmony_ci void *do_nothing_text = dereference_function_descriptor(do_nothing); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci pr_info("attempting ok execution at %px\n", do_nothing_text); 9462306a36Sopenharmony_ci do_nothing(); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci copied = access_process_vm(current, (unsigned long)dst, do_nothing_text, 9762306a36Sopenharmony_ci EXEC_SIZE, FOLL_WRITE); 9862306a36Sopenharmony_ci if (copied < EXEC_SIZE) 9962306a36Sopenharmony_ci return; 10062306a36Sopenharmony_ci pr_info("attempting bad execution at %px\n", dst); 10162306a36Sopenharmony_ci func = setup_function_descriptor(&fdesc, dst); 10262306a36Sopenharmony_ci func(); 10362306a36Sopenharmony_ci pr_err("FAIL: func returned\n"); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void lkdtm_WRITE_RO(void) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci /* Explicitly cast away "const" for the test and make volatile. */ 10962306a36Sopenharmony_ci volatile unsigned long *ptr = (unsigned long *)&rodata; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci pr_info("attempting bad rodata write at %px\n", ptr); 11262306a36Sopenharmony_ci *ptr ^= 0xabcd1234; 11362306a36Sopenharmony_ci pr_err("FAIL: survived bad write\n"); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void lkdtm_WRITE_RO_AFTER_INIT(void) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci volatile unsigned long *ptr = &ro_after_init; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * Verify we were written to during init. Since an Oops 12262306a36Sopenharmony_ci * is considered a "success", a failure is to just skip the 12362306a36Sopenharmony_ci * real test. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci if ((*ptr & 0xAA) != 0xAA) { 12662306a36Sopenharmony_ci pr_info("%p was NOT written during init!?\n", ptr); 12762306a36Sopenharmony_ci return; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci pr_info("attempting bad ro_after_init write at %px\n", ptr); 13162306a36Sopenharmony_ci *ptr ^= 0xabcd1234; 13262306a36Sopenharmony_ci pr_err("FAIL: survived bad write\n"); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void lkdtm_WRITE_KERN(void) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci size_t size; 13862306a36Sopenharmony_ci volatile unsigned char *ptr; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci size = (unsigned long)dereference_function_descriptor(do_overwritten) - 14162306a36Sopenharmony_ci (unsigned long)dereference_function_descriptor(do_nothing); 14262306a36Sopenharmony_ci ptr = dereference_function_descriptor(do_overwritten); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci pr_info("attempting bad %zu byte write at %px\n", size, ptr); 14562306a36Sopenharmony_ci memcpy((void *)ptr, (unsigned char *)do_nothing, size); 14662306a36Sopenharmony_ci flush_icache_range((unsigned long)ptr, (unsigned long)(ptr + size)); 14762306a36Sopenharmony_ci pr_err("FAIL: survived bad write\n"); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci do_overwritten(); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void lkdtm_WRITE_OPD(void) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci size_t size = sizeof(func_desc_t); 15562306a36Sopenharmony_ci void (*func)(void) = do_nothing; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (!have_function_descriptors()) { 15862306a36Sopenharmony_ci pr_info("XFAIL: Platform doesn't use function descriptors.\n"); 15962306a36Sopenharmony_ci return; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci pr_info("attempting bad %zu bytes write at %px\n", size, do_nothing); 16262306a36Sopenharmony_ci memcpy(do_nothing, do_almost_nothing, size); 16362306a36Sopenharmony_ci pr_err("FAIL: survived bad write\n"); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci asm("" : "=m"(func)); 16662306a36Sopenharmony_ci func(); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void lkdtm_EXEC_DATA(void) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci execute_location(data_area, CODE_WRITE); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void lkdtm_EXEC_STACK(void) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci u8 stack_area[EXEC_SIZE]; 17762306a36Sopenharmony_ci execute_location(stack_area, CODE_WRITE); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void lkdtm_EXEC_KMALLOC(void) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci u32 *kmalloc_area = kmalloc(EXEC_SIZE, GFP_KERNEL); 18362306a36Sopenharmony_ci execute_location(kmalloc_area, CODE_WRITE); 18462306a36Sopenharmony_ci kfree(kmalloc_area); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void lkdtm_EXEC_VMALLOC(void) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci u32 *vmalloc_area = vmalloc(EXEC_SIZE); 19062306a36Sopenharmony_ci execute_location(vmalloc_area, CODE_WRITE); 19162306a36Sopenharmony_ci vfree(vmalloc_area); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void lkdtm_EXEC_RODATA(void) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci execute_location(dereference_function_descriptor(lkdtm_rodata_do_nothing), 19762306a36Sopenharmony_ci CODE_AS_IS); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void lkdtm_EXEC_USERSPACE(void) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci unsigned long user_addr; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci user_addr = vm_mmap(NULL, 0, PAGE_SIZE, 20562306a36Sopenharmony_ci PROT_READ | PROT_WRITE | PROT_EXEC, 20662306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, 0); 20762306a36Sopenharmony_ci if (user_addr >= TASK_SIZE) { 20862306a36Sopenharmony_ci pr_warn("Failed to allocate user memory\n"); 20962306a36Sopenharmony_ci return; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci execute_user_location((void *)user_addr); 21262306a36Sopenharmony_ci vm_munmap(user_addr, PAGE_SIZE); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic void lkdtm_EXEC_NULL(void) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci execute_location(NULL, CODE_AS_IS); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void lkdtm_ACCESS_USERSPACE(void) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci unsigned long user_addr, tmp = 0; 22362306a36Sopenharmony_ci unsigned long *ptr; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci user_addr = vm_mmap(NULL, 0, PAGE_SIZE, 22662306a36Sopenharmony_ci PROT_READ | PROT_WRITE | PROT_EXEC, 22762306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, 0); 22862306a36Sopenharmony_ci if (user_addr >= TASK_SIZE) { 22962306a36Sopenharmony_ci pr_warn("Failed to allocate user memory\n"); 23062306a36Sopenharmony_ci return; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (copy_to_user((void __user *)user_addr, &tmp, sizeof(tmp))) { 23462306a36Sopenharmony_ci pr_warn("copy_to_user failed\n"); 23562306a36Sopenharmony_ci vm_munmap(user_addr, PAGE_SIZE); 23662306a36Sopenharmony_ci return; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci ptr = (unsigned long *)user_addr; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci pr_info("attempting bad read at %px\n", ptr); 24262306a36Sopenharmony_ci tmp = *ptr; 24362306a36Sopenharmony_ci tmp += 0xc0dec0de; 24462306a36Sopenharmony_ci pr_err("FAIL: survived bad read\n"); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci pr_info("attempting bad write at %px\n", ptr); 24762306a36Sopenharmony_ci *ptr = tmp; 24862306a36Sopenharmony_ci pr_err("FAIL: survived bad write\n"); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci vm_munmap(user_addr, PAGE_SIZE); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void lkdtm_ACCESS_NULL(void) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci unsigned long tmp; 25662306a36Sopenharmony_ci volatile unsigned long *ptr = (unsigned long *)NULL; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci pr_info("attempting bad read at %px\n", ptr); 25962306a36Sopenharmony_ci tmp = *ptr; 26062306a36Sopenharmony_ci tmp += 0xc0dec0de; 26162306a36Sopenharmony_ci pr_err("FAIL: survived bad read\n"); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci pr_info("attempting bad write at %px\n", ptr); 26462306a36Sopenharmony_ci *ptr = tmp; 26562306a36Sopenharmony_ci pr_err("FAIL: survived bad write\n"); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_civoid __init lkdtm_perms_init(void) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci /* Make sure we can write to __ro_after_init values during __init */ 27162306a36Sopenharmony_ci ro_after_init |= 0xAA; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic struct crashtype crashtypes[] = { 27562306a36Sopenharmony_ci CRASHTYPE(WRITE_RO), 27662306a36Sopenharmony_ci CRASHTYPE(WRITE_RO_AFTER_INIT), 27762306a36Sopenharmony_ci CRASHTYPE(WRITE_KERN), 27862306a36Sopenharmony_ci CRASHTYPE(WRITE_OPD), 27962306a36Sopenharmony_ci CRASHTYPE(EXEC_DATA), 28062306a36Sopenharmony_ci CRASHTYPE(EXEC_STACK), 28162306a36Sopenharmony_ci CRASHTYPE(EXEC_KMALLOC), 28262306a36Sopenharmony_ci CRASHTYPE(EXEC_VMALLOC), 28362306a36Sopenharmony_ci CRASHTYPE(EXEC_RODATA), 28462306a36Sopenharmony_ci CRASHTYPE(EXEC_USERSPACE), 28562306a36Sopenharmony_ci CRASHTYPE(EXEC_NULL), 28662306a36Sopenharmony_ci CRASHTYPE(ACCESS_USERSPACE), 28762306a36Sopenharmony_ci CRASHTYPE(ACCESS_NULL), 28862306a36Sopenharmony_ci}; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistruct crashtype_category perms_crashtypes = { 29162306a36Sopenharmony_ci .crashtypes = crashtypes, 29262306a36Sopenharmony_ci .len = ARRAY_SIZE(crashtypes), 29362306a36Sopenharmony_ci}; 294