18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This is for all the tests related to logic bugs (e.g. bad dereferences, 48c2ecf20Sopenharmony_ci * bad alignment, bad loops, bad locking, bad scheduling, deep stacks, and 58c2ecf20Sopenharmony_ci * lockups) along with other things that don't fit well into existing LKDTM 68c2ecf20Sopenharmony_ci * test source files. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include "lkdtm.h" 98c2ecf20Sopenharmony_ci#include <linux/list.h> 108c2ecf20Sopenharmony_ci#include <linux/sched.h> 118c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 128c2ecf20Sopenharmony_ci#include <linux/sched/task_stack.h> 138c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_X86_32) && !IS_ENABLED(CONFIG_UML) 178c2ecf20Sopenharmony_ci#include <asm/desc.h> 188c2ecf20Sopenharmony_ci#endif 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct lkdtm_list { 218c2ecf20Sopenharmony_ci struct list_head node; 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * Make sure our attempts to over run the kernel stack doesn't trigger 268c2ecf20Sopenharmony_ci * a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we 278c2ecf20Sopenharmony_ci * recurse past the end of THREAD_SIZE by default. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci#if defined(CONFIG_FRAME_WARN) && (CONFIG_FRAME_WARN > 0) 308c2ecf20Sopenharmony_ci#define REC_STACK_SIZE (_AC(CONFIG_FRAME_WARN, UL) / 2) 318c2ecf20Sopenharmony_ci#else 328c2ecf20Sopenharmony_ci#define REC_STACK_SIZE (THREAD_SIZE / 8) 338c2ecf20Sopenharmony_ci#endif 348c2ecf20Sopenharmony_ci#define REC_NUM_DEFAULT ((THREAD_SIZE / REC_STACK_SIZE) * 2) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int recur_count = REC_NUM_DEFAULT; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(lock_me_up); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * Make sure compiler does not optimize this function or stack frame away: 428c2ecf20Sopenharmony_ci * - function marked noinline 438c2ecf20Sopenharmony_ci * - stack variables are marked volatile 448c2ecf20Sopenharmony_ci * - stack variables are written (memset()) and read (pr_info()) 458c2ecf20Sopenharmony_ci * - function has external effects (pr_info()) 468c2ecf20Sopenharmony_ci * */ 478c2ecf20Sopenharmony_cistatic int noinline recursive_loop(int remaining) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci volatile char buf[REC_STACK_SIZE]; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci memset((void *)buf, remaining & 0xFF, sizeof(buf)); 528c2ecf20Sopenharmony_ci pr_info("loop %d/%d ...\n", (int)buf[remaining % sizeof(buf)], 538c2ecf20Sopenharmony_ci recur_count); 548c2ecf20Sopenharmony_ci if (!remaining) 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci else 578c2ecf20Sopenharmony_ci return recursive_loop(remaining - 1); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* If the depth is negative, use the default, otherwise keep parameter. */ 618c2ecf20Sopenharmony_civoid __init lkdtm_bugs_init(int *recur_param) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci if (*recur_param < 0) 648c2ecf20Sopenharmony_ci *recur_param = recur_count; 658c2ecf20Sopenharmony_ci else 668c2ecf20Sopenharmony_ci recur_count = *recur_param; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_civoid lkdtm_PANIC(void) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci panic("dumptest"); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_civoid lkdtm_BUG(void) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci BUG(); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int warn_counter; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_civoid lkdtm_WARNING(void) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci WARN_ON(++warn_counter); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_civoid lkdtm_WARNING_MESSAGE(void) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci WARN(1, "Warning message trigger count: %d\n", ++warn_counter); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_civoid lkdtm_EXCEPTION(void) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci *((volatile int *) 0) = 0; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_civoid lkdtm_LOOP(void) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci for (;;) 998c2ecf20Sopenharmony_ci ; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_civoid lkdtm_EXHAUST_STACK(void) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci pr_info("Calling function with %lu frame size to depth %d ...\n", 1058c2ecf20Sopenharmony_ci REC_STACK_SIZE, recur_count); 1068c2ecf20Sopenharmony_ci recursive_loop(recur_count); 1078c2ecf20Sopenharmony_ci pr_info("FAIL: survived without exhausting stack?!\n"); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic noinline void __lkdtm_CORRUPT_STACK(void *stack) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci memset(stack, '\xff', 64); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* This should trip the stack canary, not corrupt the return address. */ 1168c2ecf20Sopenharmony_cinoinline void lkdtm_CORRUPT_STACK(void) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci /* Use default char array length that triggers stack protection. */ 1198c2ecf20Sopenharmony_ci char data[8] __aligned(sizeof(void *)); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci pr_info("Corrupting stack containing char array ...\n"); 1228c2ecf20Sopenharmony_ci __lkdtm_CORRUPT_STACK((void *)&data); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* Same as above but will only get a canary with -fstack-protector-strong */ 1268c2ecf20Sopenharmony_cinoinline void lkdtm_CORRUPT_STACK_STRONG(void) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci union { 1298c2ecf20Sopenharmony_ci unsigned short shorts[4]; 1308c2ecf20Sopenharmony_ci unsigned long *ptr; 1318c2ecf20Sopenharmony_ci } data __aligned(sizeof(void *)); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci pr_info("Corrupting stack containing union ...\n"); 1348c2ecf20Sopenharmony_ci __lkdtm_CORRUPT_STACK((void *)&data); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_civoid lkdtm_UNALIGNED_LOAD_STORE_WRITE(void) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci static u8 data[5] __attribute__((aligned(4))) = {1, 2, 3, 4, 5}; 1408c2ecf20Sopenharmony_ci u32 *p; 1418c2ecf20Sopenharmony_ci u32 val = 0x12345678; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci p = (u32 *)(data + 1); 1448c2ecf20Sopenharmony_ci if (*p == 0) 1458c2ecf20Sopenharmony_ci val = 0x87654321; 1468c2ecf20Sopenharmony_ci *p = val; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) 1498c2ecf20Sopenharmony_ci pr_err("XFAIL: arch has CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS\n"); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_civoid lkdtm_SOFTLOCKUP(void) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci preempt_disable(); 1558c2ecf20Sopenharmony_ci for (;;) 1568c2ecf20Sopenharmony_ci cpu_relax(); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_civoid lkdtm_HARDLOCKUP(void) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci local_irq_disable(); 1628c2ecf20Sopenharmony_ci for (;;) 1638c2ecf20Sopenharmony_ci cpu_relax(); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_civoid lkdtm_SPINLOCKUP(void) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci /* Must be called twice to trigger. */ 1698c2ecf20Sopenharmony_ci spin_lock(&lock_me_up); 1708c2ecf20Sopenharmony_ci /* Let sparse know we intended to exit holding the lock. */ 1718c2ecf20Sopenharmony_ci __release(&lock_me_up); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_civoid lkdtm_HUNG_TASK(void) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 1778c2ecf20Sopenharmony_ci schedule(); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_civolatile unsigned int huge = INT_MAX - 2; 1818c2ecf20Sopenharmony_civolatile unsigned int ignored; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_civoid lkdtm_OVERFLOW_SIGNED(void) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci int value; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci value = huge; 1888c2ecf20Sopenharmony_ci pr_info("Normal signed addition ...\n"); 1898c2ecf20Sopenharmony_ci value += 1; 1908c2ecf20Sopenharmony_ci ignored = value; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci pr_info("Overflowing signed addition ...\n"); 1938c2ecf20Sopenharmony_ci value += 4; 1948c2ecf20Sopenharmony_ci ignored = value; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_civoid lkdtm_OVERFLOW_UNSIGNED(void) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci unsigned int value; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci value = huge; 2038c2ecf20Sopenharmony_ci pr_info("Normal unsigned addition ...\n"); 2048c2ecf20Sopenharmony_ci value += 1; 2058c2ecf20Sopenharmony_ci ignored = value; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci pr_info("Overflowing unsigned addition ...\n"); 2088c2ecf20Sopenharmony_ci value += 4; 2098c2ecf20Sopenharmony_ci ignored = value; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/* Intentionally using old-style flex array definition of 1 byte. */ 2138c2ecf20Sopenharmony_cistruct array_bounds_flex_array { 2148c2ecf20Sopenharmony_ci int one; 2158c2ecf20Sopenharmony_ci int two; 2168c2ecf20Sopenharmony_ci char data[1]; 2178c2ecf20Sopenharmony_ci}; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistruct array_bounds { 2208c2ecf20Sopenharmony_ci int one; 2218c2ecf20Sopenharmony_ci int two; 2228c2ecf20Sopenharmony_ci char data[8]; 2238c2ecf20Sopenharmony_ci int three; 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_civoid lkdtm_ARRAY_BOUNDS(void) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct array_bounds_flex_array *not_checked; 2298c2ecf20Sopenharmony_ci struct array_bounds *checked; 2308c2ecf20Sopenharmony_ci volatile int i; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci not_checked = kmalloc(sizeof(*not_checked) * 2, GFP_KERNEL); 2338c2ecf20Sopenharmony_ci checked = kmalloc(sizeof(*checked) * 2, GFP_KERNEL); 2348c2ecf20Sopenharmony_ci if (!not_checked || !checked) { 2358c2ecf20Sopenharmony_ci kfree(not_checked); 2368c2ecf20Sopenharmony_ci kfree(checked); 2378c2ecf20Sopenharmony_ci return; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci pr_info("Array access within bounds ...\n"); 2418c2ecf20Sopenharmony_ci /* For both, touch all bytes in the actual member size. */ 2428c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(checked->data); i++) 2438c2ecf20Sopenharmony_ci checked->data[i] = 'A'; 2448c2ecf20Sopenharmony_ci /* 2458c2ecf20Sopenharmony_ci * For the uninstrumented flex array member, also touch 1 byte 2468c2ecf20Sopenharmony_ci * beyond to verify it is correctly uninstrumented. 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(not_checked->data) + 1; i++) 2498c2ecf20Sopenharmony_ci not_checked->data[i] = 'A'; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci pr_info("Array access beyond bounds ...\n"); 2528c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(checked->data) + 1; i++) 2538c2ecf20Sopenharmony_ci checked->data[i] = 'B'; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci kfree(not_checked); 2568c2ecf20Sopenharmony_ci kfree(checked); 2578c2ecf20Sopenharmony_ci pr_err("FAIL: survived array bounds overflow!\n"); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_civoid lkdtm_CORRUPT_LIST_ADD(void) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci /* 2638c2ecf20Sopenharmony_ci * Initially, an empty list via LIST_HEAD: 2648c2ecf20Sopenharmony_ci * test_head.next = &test_head 2658c2ecf20Sopenharmony_ci * test_head.prev = &test_head 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci LIST_HEAD(test_head); 2688c2ecf20Sopenharmony_ci struct lkdtm_list good, bad; 2698c2ecf20Sopenharmony_ci void *target[2] = { }; 2708c2ecf20Sopenharmony_ci void *redirection = ⌖ 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci pr_info("attempting good list addition\n"); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * Adding to the list performs these actions: 2768c2ecf20Sopenharmony_ci * test_head.next->prev = &good.node 2778c2ecf20Sopenharmony_ci * good.node.next = test_head.next 2788c2ecf20Sopenharmony_ci * good.node.prev = test_head 2798c2ecf20Sopenharmony_ci * test_head.next = good.node 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci list_add(&good.node, &test_head); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci pr_info("attempting corrupted list addition\n"); 2848c2ecf20Sopenharmony_ci /* 2858c2ecf20Sopenharmony_ci * In simulating this "write what where" primitive, the "what" is 2868c2ecf20Sopenharmony_ci * the address of &bad.node, and the "where" is the address held 2878c2ecf20Sopenharmony_ci * by "redirection". 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_ci test_head.next = redirection; 2908c2ecf20Sopenharmony_ci list_add(&bad.node, &test_head); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (target[0] == NULL && target[1] == NULL) 2938c2ecf20Sopenharmony_ci pr_err("Overwrite did not happen, but no BUG?!\n"); 2948c2ecf20Sopenharmony_ci else 2958c2ecf20Sopenharmony_ci pr_err("list_add() corruption not detected!\n"); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_civoid lkdtm_CORRUPT_LIST_DEL(void) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci LIST_HEAD(test_head); 3018c2ecf20Sopenharmony_ci struct lkdtm_list item; 3028c2ecf20Sopenharmony_ci void *target[2] = { }; 3038c2ecf20Sopenharmony_ci void *redirection = ⌖ 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci list_add(&item.node, &test_head); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci pr_info("attempting good list removal\n"); 3088c2ecf20Sopenharmony_ci list_del(&item.node); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci pr_info("attempting corrupted list removal\n"); 3118c2ecf20Sopenharmony_ci list_add(&item.node, &test_head); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* As with the list_add() test above, this corrupts "next". */ 3148c2ecf20Sopenharmony_ci item.node.next = redirection; 3158c2ecf20Sopenharmony_ci list_del(&item.node); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (target[0] == NULL && target[1] == NULL) 3188c2ecf20Sopenharmony_ci pr_err("Overwrite did not happen, but no BUG?!\n"); 3198c2ecf20Sopenharmony_ci else 3208c2ecf20Sopenharmony_ci pr_err("list_del() corruption not detected!\n"); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci/* Test that VMAP_STACK is actually allocating with a leading guard page */ 3248c2ecf20Sopenharmony_civoid lkdtm_STACK_GUARD_PAGE_LEADING(void) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci const unsigned char *stack = task_stack_page(current); 3278c2ecf20Sopenharmony_ci const unsigned char *ptr = stack - 1; 3288c2ecf20Sopenharmony_ci volatile unsigned char byte; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci pr_info("attempting bad read from page below current stack\n"); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci byte = *ptr; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci pr_err("FAIL: accessed page before stack! (byte: %x)\n", byte); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/* Test that VMAP_STACK is actually allocating with a trailing guard page */ 3388c2ecf20Sopenharmony_civoid lkdtm_STACK_GUARD_PAGE_TRAILING(void) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci const unsigned char *stack = task_stack_page(current); 3418c2ecf20Sopenharmony_ci const unsigned char *ptr = stack + THREAD_SIZE; 3428c2ecf20Sopenharmony_ci volatile unsigned char byte; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci pr_info("attempting bad read from page above current stack\n"); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci byte = *ptr; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci pr_err("FAIL: accessed page after stack! (byte: %x)\n", byte); 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_civoid lkdtm_UNSET_SMEP(void) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_X86_64) && !IS_ENABLED(CONFIG_UML) 3548c2ecf20Sopenharmony_ci#define MOV_CR4_DEPTH 64 3558c2ecf20Sopenharmony_ci void (*direct_write_cr4)(unsigned long val); 3568c2ecf20Sopenharmony_ci unsigned char *insn; 3578c2ecf20Sopenharmony_ci unsigned long cr4; 3588c2ecf20Sopenharmony_ci int i; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci cr4 = native_read_cr4(); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if ((cr4 & X86_CR4_SMEP) != X86_CR4_SMEP) { 3638c2ecf20Sopenharmony_ci pr_err("FAIL: SMEP not in use\n"); 3648c2ecf20Sopenharmony_ci return; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci cr4 &= ~(X86_CR4_SMEP); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci pr_info("trying to clear SMEP normally\n"); 3698c2ecf20Sopenharmony_ci native_write_cr4(cr4); 3708c2ecf20Sopenharmony_ci if (cr4 == native_read_cr4()) { 3718c2ecf20Sopenharmony_ci pr_err("FAIL: pinning SMEP failed!\n"); 3728c2ecf20Sopenharmony_ci cr4 |= X86_CR4_SMEP; 3738c2ecf20Sopenharmony_ci pr_info("restoring SMEP\n"); 3748c2ecf20Sopenharmony_ci native_write_cr4(cr4); 3758c2ecf20Sopenharmony_ci return; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci pr_info("ok: SMEP did not get cleared\n"); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* 3808c2ecf20Sopenharmony_ci * To test the post-write pinning verification we need to call 3818c2ecf20Sopenharmony_ci * directly into the middle of native_write_cr4() where the 3828c2ecf20Sopenharmony_ci * cr4 write happens, skipping any pinning. This searches for 3838c2ecf20Sopenharmony_ci * the cr4 writing instruction. 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_ci insn = (unsigned char *)native_write_cr4; 3868c2ecf20Sopenharmony_ci for (i = 0; i < MOV_CR4_DEPTH; i++) { 3878c2ecf20Sopenharmony_ci /* mov %rdi, %cr4 */ 3888c2ecf20Sopenharmony_ci if (insn[i] == 0x0f && insn[i+1] == 0x22 && insn[i+2] == 0xe7) 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci /* mov %rdi,%rax; mov %rax, %cr4 */ 3918c2ecf20Sopenharmony_ci if (insn[i] == 0x48 && insn[i+1] == 0x89 && 3928c2ecf20Sopenharmony_ci insn[i+2] == 0xf8 && insn[i+3] == 0x0f && 3938c2ecf20Sopenharmony_ci insn[i+4] == 0x22 && insn[i+5] == 0xe0) 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci if (i >= MOV_CR4_DEPTH) { 3978c2ecf20Sopenharmony_ci pr_info("ok: cannot locate cr4 writing call gadget\n"); 3988c2ecf20Sopenharmony_ci return; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci direct_write_cr4 = (void *)(insn + i); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci pr_info("trying to clear SMEP with call gadget\n"); 4038c2ecf20Sopenharmony_ci direct_write_cr4(cr4); 4048c2ecf20Sopenharmony_ci if (native_read_cr4() & X86_CR4_SMEP) { 4058c2ecf20Sopenharmony_ci pr_info("ok: SMEP removal was reverted\n"); 4068c2ecf20Sopenharmony_ci } else { 4078c2ecf20Sopenharmony_ci pr_err("FAIL: cleared SMEP not detected!\n"); 4088c2ecf20Sopenharmony_ci cr4 |= X86_CR4_SMEP; 4098c2ecf20Sopenharmony_ci pr_info("restoring SMEP\n"); 4108c2ecf20Sopenharmony_ci native_write_cr4(cr4); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci#else 4138c2ecf20Sopenharmony_ci pr_err("XFAIL: this test is x86_64-only\n"); 4148c2ecf20Sopenharmony_ci#endif 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_civoid lkdtm_DOUBLE_FAULT(void) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_X86_32) && !IS_ENABLED(CONFIG_UML) 4208c2ecf20Sopenharmony_ci /* 4218c2ecf20Sopenharmony_ci * Trigger #DF by setting the stack limit to zero. This clobbers 4228c2ecf20Sopenharmony_ci * a GDT TLS slot, which is okay because the current task will die 4238c2ecf20Sopenharmony_ci * anyway due to the double fault. 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_ci struct desc_struct d = { 4268c2ecf20Sopenharmony_ci .type = 3, /* expand-up, writable, accessed data */ 4278c2ecf20Sopenharmony_ci .p = 1, /* present */ 4288c2ecf20Sopenharmony_ci .d = 1, /* 32-bit */ 4298c2ecf20Sopenharmony_ci .g = 0, /* limit in bytes */ 4308c2ecf20Sopenharmony_ci .s = 1, /* not system */ 4318c2ecf20Sopenharmony_ci }; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci local_irq_disable(); 4348c2ecf20Sopenharmony_ci write_gdt_entry(get_cpu_gdt_rw(smp_processor_id()), 4358c2ecf20Sopenharmony_ci GDT_ENTRY_TLS_MIN, &d, DESCTYPE_S); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* 4388c2ecf20Sopenharmony_ci * Put our zero-limit segment in SS and then trigger a fault. The 4398c2ecf20Sopenharmony_ci * 4-byte access to (%esp) will fault with #SS, and the attempt to 4408c2ecf20Sopenharmony_ci * deliver the fault will recursively cause #SS and result in #DF. 4418c2ecf20Sopenharmony_ci * This whole process happens while NMIs and MCEs are blocked by the 4428c2ecf20Sopenharmony_ci * MOV SS window. This is nice because an NMI with an invalid SS 4438c2ecf20Sopenharmony_ci * would also double-fault, resulting in the NMI or MCE being lost. 4448c2ecf20Sopenharmony_ci */ 4458c2ecf20Sopenharmony_ci asm volatile ("movw %0, %%ss; addl $0, (%%esp)" :: 4468c2ecf20Sopenharmony_ci "r" ((unsigned short)(GDT_ENTRY_TLS_MIN << 3))); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci pr_err("FAIL: tried to double fault but didn't die\n"); 4498c2ecf20Sopenharmony_ci#else 4508c2ecf20Sopenharmony_ci pr_err("XFAIL: this test is ia32-only\n"); 4518c2ecf20Sopenharmony_ci#endif 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64 4558c2ecf20Sopenharmony_cistatic noinline void change_pac_parameters(void) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH)) { 4588c2ecf20Sopenharmony_ci /* Reset the keys of current task */ 4598c2ecf20Sopenharmony_ci ptrauth_thread_init_kernel(current); 4608c2ecf20Sopenharmony_ci ptrauth_thread_switch_kernel(current); 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci#endif 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cinoinline void lkdtm_CORRUPT_PAC(void) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64 4688c2ecf20Sopenharmony_ci#define CORRUPT_PAC_ITERATE 10 4698c2ecf20Sopenharmony_ci int i; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_ARM64_PTR_AUTH)) 4728c2ecf20Sopenharmony_ci pr_err("FAIL: kernel not built with CONFIG_ARM64_PTR_AUTH\n"); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (!system_supports_address_auth()) { 4758c2ecf20Sopenharmony_ci pr_err("FAIL: CPU lacks pointer authentication feature\n"); 4768c2ecf20Sopenharmony_ci return; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci pr_info("changing PAC parameters to force function return failure...\n"); 4808c2ecf20Sopenharmony_ci /* 4818c2ecf20Sopenharmony_ci * PAC is a hash value computed from input keys, return address and 4828c2ecf20Sopenharmony_ci * stack pointer. As pac has fewer bits so there is a chance of 4838c2ecf20Sopenharmony_ci * collision, so iterate few times to reduce the collision probability. 4848c2ecf20Sopenharmony_ci */ 4858c2ecf20Sopenharmony_ci for (i = 0; i < CORRUPT_PAC_ITERATE; i++) 4868c2ecf20Sopenharmony_ci change_pac_parameters(); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci pr_err("FAIL: survived PAC changes! Kernel may be unstable from here\n"); 4898c2ecf20Sopenharmony_ci#else 4908c2ecf20Sopenharmony_ci pr_err("XFAIL: this test is arm64-only\n"); 4918c2ecf20Sopenharmony_ci#endif 4928c2ecf20Sopenharmony_ci} 493