162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This is for all the tests related to logic bugs (e.g. bad dereferences, 462306a36Sopenharmony_ci * bad alignment, bad loops, bad locking, bad scheduling, deep stacks, and 562306a36Sopenharmony_ci * lockups) along with other things that don't fit well into existing LKDTM 662306a36Sopenharmony_ci * test source files. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include "lkdtm.h" 962306a36Sopenharmony_ci#include <linux/list.h> 1062306a36Sopenharmony_ci#include <linux/sched.h> 1162306a36Sopenharmony_ci#include <linux/sched/signal.h> 1262306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 1362306a36Sopenharmony_ci#include <linux/uaccess.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_X86_32) && !IS_ENABLED(CONFIG_UML) 1762306a36Sopenharmony_ci#include <asm/desc.h> 1862306a36Sopenharmony_ci#endif 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct lkdtm_list { 2162306a36Sopenharmony_ci struct list_head node; 2262306a36Sopenharmony_ci}; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Make sure our attempts to over run the kernel stack doesn't trigger 2662306a36Sopenharmony_ci * a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we 2762306a36Sopenharmony_ci * recurse past the end of THREAD_SIZE by default. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci#if defined(CONFIG_FRAME_WARN) && (CONFIG_FRAME_WARN > 0) 3062306a36Sopenharmony_ci#define REC_STACK_SIZE (_AC(CONFIG_FRAME_WARN, UL) / 2) 3162306a36Sopenharmony_ci#else 3262306a36Sopenharmony_ci#define REC_STACK_SIZE (THREAD_SIZE / 8UL) 3362306a36Sopenharmony_ci#endif 3462306a36Sopenharmony_ci#define REC_NUM_DEFAULT ((THREAD_SIZE / REC_STACK_SIZE) * 2) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int recur_count = REC_NUM_DEFAULT; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(lock_me_up); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* 4162306a36Sopenharmony_ci * Make sure compiler does not optimize this function or stack frame away: 4262306a36Sopenharmony_ci * - function marked noinline 4362306a36Sopenharmony_ci * - stack variables are marked volatile 4462306a36Sopenharmony_ci * - stack variables are written (memset()) and read (buf[..] passed as arg) 4562306a36Sopenharmony_ci * - function may have external effects (memzero_explicit()) 4662306a36Sopenharmony_ci * - no tail recursion possible 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cistatic int noinline recursive_loop(int remaining) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci volatile char buf[REC_STACK_SIZE]; 5162306a36Sopenharmony_ci volatile int ret; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci memset((void *)buf, remaining & 0xFF, sizeof(buf)); 5462306a36Sopenharmony_ci if (!remaining) 5562306a36Sopenharmony_ci ret = 0; 5662306a36Sopenharmony_ci else 5762306a36Sopenharmony_ci ret = recursive_loop((int)buf[remaining % sizeof(buf)] - 1); 5862306a36Sopenharmony_ci memzero_explicit((void *)buf, sizeof(buf)); 5962306a36Sopenharmony_ci return ret; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* If the depth is negative, use the default, otherwise keep parameter. */ 6362306a36Sopenharmony_civoid __init lkdtm_bugs_init(int *recur_param) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci if (*recur_param < 0) 6662306a36Sopenharmony_ci *recur_param = recur_count; 6762306a36Sopenharmony_ci else 6862306a36Sopenharmony_ci recur_count = *recur_param; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void lkdtm_PANIC(void) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci panic("dumptest"); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void lkdtm_BUG(void) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci BUG(); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int warn_counter; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void lkdtm_WARNING(void) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci WARN_ON(++warn_counter); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void lkdtm_WARNING_MESSAGE(void) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci WARN(1, "Warning message trigger count: %d\n", ++warn_counter); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void lkdtm_EXCEPTION(void) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci *((volatile int *) 0) = 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void lkdtm_LOOP(void) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci for (;;) 10162306a36Sopenharmony_ci ; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void lkdtm_EXHAUST_STACK(void) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci pr_info("Calling function with %lu frame size to depth %d ...\n", 10762306a36Sopenharmony_ci REC_STACK_SIZE, recur_count); 10862306a36Sopenharmony_ci recursive_loop(recur_count); 10962306a36Sopenharmony_ci pr_info("FAIL: survived without exhausting stack?!\n"); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic noinline void __lkdtm_CORRUPT_STACK(void *stack) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci memset(stack, '\xff', 64); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* This should trip the stack canary, not corrupt the return address. */ 11862306a36Sopenharmony_cistatic noinline void lkdtm_CORRUPT_STACK(void) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci /* Use default char array length that triggers stack protection. */ 12162306a36Sopenharmony_ci char data[8] __aligned(sizeof(void *)); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci pr_info("Corrupting stack containing char array ...\n"); 12462306a36Sopenharmony_ci __lkdtm_CORRUPT_STACK((void *)&data); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* Same as above but will only get a canary with -fstack-protector-strong */ 12862306a36Sopenharmony_cistatic noinline void lkdtm_CORRUPT_STACK_STRONG(void) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci union { 13162306a36Sopenharmony_ci unsigned short shorts[4]; 13262306a36Sopenharmony_ci unsigned long *ptr; 13362306a36Sopenharmony_ci } data __aligned(sizeof(void *)); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci pr_info("Corrupting stack containing union ...\n"); 13662306a36Sopenharmony_ci __lkdtm_CORRUPT_STACK((void *)&data); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic pid_t stack_pid; 14062306a36Sopenharmony_cistatic unsigned long stack_addr; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void lkdtm_REPORT_STACK(void) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci volatile uintptr_t magic; 14562306a36Sopenharmony_ci pid_t pid = task_pid_nr(current); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (pid != stack_pid) { 14862306a36Sopenharmony_ci pr_info("Starting stack offset tracking for pid %d\n", pid); 14962306a36Sopenharmony_ci stack_pid = pid; 15062306a36Sopenharmony_ci stack_addr = (uintptr_t)&magic; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci pr_info("Stack offset: %d\n", (int)(stack_addr - (uintptr_t)&magic)); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic pid_t stack_canary_pid; 15762306a36Sopenharmony_cistatic unsigned long stack_canary; 15862306a36Sopenharmony_cistatic unsigned long stack_canary_offset; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic noinline void __lkdtm_REPORT_STACK_CANARY(void *stack) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci int i = 0; 16362306a36Sopenharmony_ci pid_t pid = task_pid_nr(current); 16462306a36Sopenharmony_ci unsigned long *canary = (unsigned long *)stack; 16562306a36Sopenharmony_ci unsigned long current_offset = 0, init_offset = 0; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* Do our best to find the canary in a 16 word window ... */ 16862306a36Sopenharmony_ci for (i = 1; i < 16; i++) { 16962306a36Sopenharmony_ci canary = (unsigned long *)stack + i; 17062306a36Sopenharmony_ci#ifdef CONFIG_STACKPROTECTOR 17162306a36Sopenharmony_ci if (*canary == current->stack_canary) 17262306a36Sopenharmony_ci current_offset = i; 17362306a36Sopenharmony_ci if (*canary == init_task.stack_canary) 17462306a36Sopenharmony_ci init_offset = i; 17562306a36Sopenharmony_ci#endif 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (current_offset == 0) { 17962306a36Sopenharmony_ci /* 18062306a36Sopenharmony_ci * If the canary doesn't match what's in the task_struct, 18162306a36Sopenharmony_ci * we're either using a global canary or the stack frame 18262306a36Sopenharmony_ci * layout changed. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ci if (init_offset != 0) { 18562306a36Sopenharmony_ci pr_err("FAIL: global stack canary found at offset %ld (canary for pid %d matches init_task's)!\n", 18662306a36Sopenharmony_ci init_offset, pid); 18762306a36Sopenharmony_ci } else { 18862306a36Sopenharmony_ci pr_warn("FAIL: did not correctly locate stack canary :(\n"); 18962306a36Sopenharmony_ci pr_expected_config(CONFIG_STACKPROTECTOR); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return; 19362306a36Sopenharmony_ci } else if (init_offset != 0) { 19462306a36Sopenharmony_ci pr_warn("WARNING: found both current and init_task canaries nearby?!\n"); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci canary = (unsigned long *)stack + current_offset; 19862306a36Sopenharmony_ci if (stack_canary_pid == 0) { 19962306a36Sopenharmony_ci stack_canary = *canary; 20062306a36Sopenharmony_ci stack_canary_pid = pid; 20162306a36Sopenharmony_ci stack_canary_offset = current_offset; 20262306a36Sopenharmony_ci pr_info("Recorded stack canary for pid %d at offset %ld\n", 20362306a36Sopenharmony_ci stack_canary_pid, stack_canary_offset); 20462306a36Sopenharmony_ci } else if (pid == stack_canary_pid) { 20562306a36Sopenharmony_ci pr_warn("ERROR: saw pid %d again -- please use a new pid\n", pid); 20662306a36Sopenharmony_ci } else { 20762306a36Sopenharmony_ci if (current_offset != stack_canary_offset) { 20862306a36Sopenharmony_ci pr_warn("ERROR: canary offset changed from %ld to %ld!?\n", 20962306a36Sopenharmony_ci stack_canary_offset, current_offset); 21062306a36Sopenharmony_ci return; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (*canary == stack_canary) { 21462306a36Sopenharmony_ci pr_warn("FAIL: canary identical for pid %d and pid %d at offset %ld!\n", 21562306a36Sopenharmony_ci stack_canary_pid, pid, current_offset); 21662306a36Sopenharmony_ci } else { 21762306a36Sopenharmony_ci pr_info("ok: stack canaries differ between pid %d and pid %d at offset %ld.\n", 21862306a36Sopenharmony_ci stack_canary_pid, pid, current_offset); 21962306a36Sopenharmony_ci /* Reset the test. */ 22062306a36Sopenharmony_ci stack_canary_pid = 0; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void lkdtm_REPORT_STACK_CANARY(void) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci /* Use default char array length that triggers stack protection. */ 22862306a36Sopenharmony_ci char data[8] __aligned(sizeof(void *)) = { }; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci __lkdtm_REPORT_STACK_CANARY((void *)&data); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci static u8 data[5] __attribute__((aligned(4))) = {1, 2, 3, 4, 5}; 23662306a36Sopenharmony_ci u32 *p; 23762306a36Sopenharmony_ci u32 val = 0x12345678; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci p = (u32 *)(data + 1); 24062306a36Sopenharmony_ci if (*p == 0) 24162306a36Sopenharmony_ci val = 0x87654321; 24262306a36Sopenharmony_ci *p = val; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) 24562306a36Sopenharmony_ci pr_err("XFAIL: arch has CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS\n"); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void lkdtm_SOFTLOCKUP(void) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci preempt_disable(); 25162306a36Sopenharmony_ci for (;;) 25262306a36Sopenharmony_ci cpu_relax(); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic void lkdtm_HARDLOCKUP(void) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci local_irq_disable(); 25862306a36Sopenharmony_ci for (;;) 25962306a36Sopenharmony_ci cpu_relax(); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic void lkdtm_SPINLOCKUP(void) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci /* Must be called twice to trigger. */ 26562306a36Sopenharmony_ci spin_lock(&lock_me_up); 26662306a36Sopenharmony_ci /* Let sparse know we intended to exit holding the lock. */ 26762306a36Sopenharmony_ci __release(&lock_me_up); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void lkdtm_HUNG_TASK(void) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 27362306a36Sopenharmony_ci schedule(); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic volatile unsigned int huge = INT_MAX - 2; 27762306a36Sopenharmony_cistatic volatile unsigned int ignored; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic void lkdtm_OVERFLOW_SIGNED(void) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci int value; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci value = huge; 28462306a36Sopenharmony_ci pr_info("Normal signed addition ...\n"); 28562306a36Sopenharmony_ci value += 1; 28662306a36Sopenharmony_ci ignored = value; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci pr_info("Overflowing signed addition ...\n"); 28962306a36Sopenharmony_ci value += 4; 29062306a36Sopenharmony_ci ignored = value; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void lkdtm_OVERFLOW_UNSIGNED(void) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci unsigned int value; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci value = huge; 29962306a36Sopenharmony_ci pr_info("Normal unsigned addition ...\n"); 30062306a36Sopenharmony_ci value += 1; 30162306a36Sopenharmony_ci ignored = value; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci pr_info("Overflowing unsigned addition ...\n"); 30462306a36Sopenharmony_ci value += 4; 30562306a36Sopenharmony_ci ignored = value; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/* Intentionally using unannotated flex array definition. */ 30962306a36Sopenharmony_cistruct array_bounds_flex_array { 31062306a36Sopenharmony_ci int one; 31162306a36Sopenharmony_ci int two; 31262306a36Sopenharmony_ci char data[]; 31362306a36Sopenharmony_ci}; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistruct array_bounds { 31662306a36Sopenharmony_ci int one; 31762306a36Sopenharmony_ci int two; 31862306a36Sopenharmony_ci char data[8]; 31962306a36Sopenharmony_ci int three; 32062306a36Sopenharmony_ci}; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void lkdtm_ARRAY_BOUNDS(void) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct array_bounds_flex_array *not_checked; 32562306a36Sopenharmony_ci struct array_bounds *checked; 32662306a36Sopenharmony_ci volatile int i; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci not_checked = kmalloc(sizeof(*not_checked) * 2, GFP_KERNEL); 32962306a36Sopenharmony_ci checked = kmalloc(sizeof(*checked) * 2, GFP_KERNEL); 33062306a36Sopenharmony_ci if (!not_checked || !checked) { 33162306a36Sopenharmony_ci kfree(not_checked); 33262306a36Sopenharmony_ci kfree(checked); 33362306a36Sopenharmony_ci return; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci pr_info("Array access within bounds ...\n"); 33762306a36Sopenharmony_ci /* For both, touch all bytes in the actual member size. */ 33862306a36Sopenharmony_ci for (i = 0; i < sizeof(checked->data); i++) 33962306a36Sopenharmony_ci checked->data[i] = 'A'; 34062306a36Sopenharmony_ci /* 34162306a36Sopenharmony_ci * For the uninstrumented flex array member, also touch 1 byte 34262306a36Sopenharmony_ci * beyond to verify it is correctly uninstrumented. 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_ci for (i = 0; i < 2; i++) 34562306a36Sopenharmony_ci not_checked->data[i] = 'A'; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci pr_info("Array access beyond bounds ...\n"); 34862306a36Sopenharmony_ci for (i = 0; i < sizeof(checked->data) + 1; i++) 34962306a36Sopenharmony_ci checked->data[i] = 'B'; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci kfree(not_checked); 35262306a36Sopenharmony_ci kfree(checked); 35362306a36Sopenharmony_ci pr_err("FAIL: survived array bounds overflow!\n"); 35462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_UBSAN_BOUNDS)) 35562306a36Sopenharmony_ci pr_expected_config(CONFIG_UBSAN_TRAP); 35662306a36Sopenharmony_ci else 35762306a36Sopenharmony_ci pr_expected_config(CONFIG_UBSAN_BOUNDS); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistruct lkdtm_annotated { 36162306a36Sopenharmony_ci unsigned long flags; 36262306a36Sopenharmony_ci int count; 36362306a36Sopenharmony_ci int array[] __counted_by(count); 36462306a36Sopenharmony_ci}; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic volatile int fam_count = 4; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic void lkdtm_FAM_BOUNDS(void) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct lkdtm_annotated *inst; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci inst = kzalloc(struct_size(inst, array, fam_count + 1), GFP_KERNEL); 37362306a36Sopenharmony_ci if (!inst) { 37462306a36Sopenharmony_ci pr_err("FAIL: could not allocate test struct!\n"); 37562306a36Sopenharmony_ci return; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci inst->count = fam_count; 37962306a36Sopenharmony_ci pr_info("Array access within bounds ...\n"); 38062306a36Sopenharmony_ci inst->array[1] = fam_count; 38162306a36Sopenharmony_ci ignored = inst->array[1]; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci pr_info("Array access beyond bounds ...\n"); 38462306a36Sopenharmony_ci inst->array[fam_count] = fam_count; 38562306a36Sopenharmony_ci ignored = inst->array[fam_count]; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci kfree(inst); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci pr_err("FAIL: survived access of invalid flexible array member index!\n"); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (!__has_attribute(__counted_by__)) 39262306a36Sopenharmony_ci pr_warn("This is expected since this %s was built a compiler supporting __counted_by\n", 39362306a36Sopenharmony_ci lkdtm_kernel_info); 39462306a36Sopenharmony_ci else if (IS_ENABLED(CONFIG_UBSAN_BOUNDS)) 39562306a36Sopenharmony_ci pr_expected_config(CONFIG_UBSAN_TRAP); 39662306a36Sopenharmony_ci else 39762306a36Sopenharmony_ci pr_expected_config(CONFIG_UBSAN_BOUNDS); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic void lkdtm_CORRUPT_LIST_ADD(void) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci /* 40362306a36Sopenharmony_ci * Initially, an empty list via LIST_HEAD: 40462306a36Sopenharmony_ci * test_head.next = &test_head 40562306a36Sopenharmony_ci * test_head.prev = &test_head 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_ci LIST_HEAD(test_head); 40862306a36Sopenharmony_ci struct lkdtm_list good, bad; 40962306a36Sopenharmony_ci void *target[2] = { }; 41062306a36Sopenharmony_ci void *redirection = ⌖ 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci pr_info("attempting good list addition\n"); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* 41562306a36Sopenharmony_ci * Adding to the list performs these actions: 41662306a36Sopenharmony_ci * test_head.next->prev = &good.node 41762306a36Sopenharmony_ci * good.node.next = test_head.next 41862306a36Sopenharmony_ci * good.node.prev = test_head 41962306a36Sopenharmony_ci * test_head.next = good.node 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci list_add(&good.node, &test_head); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci pr_info("attempting corrupted list addition\n"); 42462306a36Sopenharmony_ci /* 42562306a36Sopenharmony_ci * In simulating this "write what where" primitive, the "what" is 42662306a36Sopenharmony_ci * the address of &bad.node, and the "where" is the address held 42762306a36Sopenharmony_ci * by "redirection". 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci test_head.next = redirection; 43062306a36Sopenharmony_ci list_add(&bad.node, &test_head); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (target[0] == NULL && target[1] == NULL) 43362306a36Sopenharmony_ci pr_err("Overwrite did not happen, but no BUG?!\n"); 43462306a36Sopenharmony_ci else { 43562306a36Sopenharmony_ci pr_err("list_add() corruption not detected!\n"); 43662306a36Sopenharmony_ci pr_expected_config(CONFIG_LIST_HARDENED); 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic void lkdtm_CORRUPT_LIST_DEL(void) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci LIST_HEAD(test_head); 44362306a36Sopenharmony_ci struct lkdtm_list item; 44462306a36Sopenharmony_ci void *target[2] = { }; 44562306a36Sopenharmony_ci void *redirection = ⌖ 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci list_add(&item.node, &test_head); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci pr_info("attempting good list removal\n"); 45062306a36Sopenharmony_ci list_del(&item.node); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci pr_info("attempting corrupted list removal\n"); 45362306a36Sopenharmony_ci list_add(&item.node, &test_head); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* As with the list_add() test above, this corrupts "next". */ 45662306a36Sopenharmony_ci item.node.next = redirection; 45762306a36Sopenharmony_ci list_del(&item.node); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (target[0] == NULL && target[1] == NULL) 46062306a36Sopenharmony_ci pr_err("Overwrite did not happen, but no BUG?!\n"); 46162306a36Sopenharmony_ci else { 46262306a36Sopenharmony_ci pr_err("list_del() corruption not detected!\n"); 46362306a36Sopenharmony_ci pr_expected_config(CONFIG_LIST_HARDENED); 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci/* Test that VMAP_STACK is actually allocating with a leading guard page */ 46862306a36Sopenharmony_cistatic void lkdtm_STACK_GUARD_PAGE_LEADING(void) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci const unsigned char *stack = task_stack_page(current); 47162306a36Sopenharmony_ci const unsigned char *ptr = stack - 1; 47262306a36Sopenharmony_ci volatile unsigned char byte; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci pr_info("attempting bad read from page below current stack\n"); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci byte = *ptr; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci pr_err("FAIL: accessed page before stack! (byte: %x)\n", byte); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci/* Test that VMAP_STACK is actually allocating with a trailing guard page */ 48262306a36Sopenharmony_cistatic void lkdtm_STACK_GUARD_PAGE_TRAILING(void) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci const unsigned char *stack = task_stack_page(current); 48562306a36Sopenharmony_ci const unsigned char *ptr = stack + THREAD_SIZE; 48662306a36Sopenharmony_ci volatile unsigned char byte; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci pr_info("attempting bad read from page above current stack\n"); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci byte = *ptr; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci pr_err("FAIL: accessed page after stack! (byte: %x)\n", byte); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic void lkdtm_UNSET_SMEP(void) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_X86_64) && !IS_ENABLED(CONFIG_UML) 49862306a36Sopenharmony_ci#define MOV_CR4_DEPTH 64 49962306a36Sopenharmony_ci void (*direct_write_cr4)(unsigned long val); 50062306a36Sopenharmony_ci unsigned char *insn; 50162306a36Sopenharmony_ci unsigned long cr4; 50262306a36Sopenharmony_ci int i; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci cr4 = native_read_cr4(); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if ((cr4 & X86_CR4_SMEP) != X86_CR4_SMEP) { 50762306a36Sopenharmony_ci pr_err("FAIL: SMEP not in use\n"); 50862306a36Sopenharmony_ci return; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci cr4 &= ~(X86_CR4_SMEP); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci pr_info("trying to clear SMEP normally\n"); 51362306a36Sopenharmony_ci native_write_cr4(cr4); 51462306a36Sopenharmony_ci if (cr4 == native_read_cr4()) { 51562306a36Sopenharmony_ci pr_err("FAIL: pinning SMEP failed!\n"); 51662306a36Sopenharmony_ci cr4 |= X86_CR4_SMEP; 51762306a36Sopenharmony_ci pr_info("restoring SMEP\n"); 51862306a36Sopenharmony_ci native_write_cr4(cr4); 51962306a36Sopenharmony_ci return; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci pr_info("ok: SMEP did not get cleared\n"); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* 52462306a36Sopenharmony_ci * To test the post-write pinning verification we need to call 52562306a36Sopenharmony_ci * directly into the middle of native_write_cr4() where the 52662306a36Sopenharmony_ci * cr4 write happens, skipping any pinning. This searches for 52762306a36Sopenharmony_ci * the cr4 writing instruction. 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_ci insn = (unsigned char *)native_write_cr4; 53062306a36Sopenharmony_ci OPTIMIZER_HIDE_VAR(insn); 53162306a36Sopenharmony_ci for (i = 0; i < MOV_CR4_DEPTH; i++) { 53262306a36Sopenharmony_ci /* mov %rdi, %cr4 */ 53362306a36Sopenharmony_ci if (insn[i] == 0x0f && insn[i+1] == 0x22 && insn[i+2] == 0xe7) 53462306a36Sopenharmony_ci break; 53562306a36Sopenharmony_ci /* mov %rdi,%rax; mov %rax, %cr4 */ 53662306a36Sopenharmony_ci if (insn[i] == 0x48 && insn[i+1] == 0x89 && 53762306a36Sopenharmony_ci insn[i+2] == 0xf8 && insn[i+3] == 0x0f && 53862306a36Sopenharmony_ci insn[i+4] == 0x22 && insn[i+5] == 0xe0) 53962306a36Sopenharmony_ci break; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci if (i >= MOV_CR4_DEPTH) { 54262306a36Sopenharmony_ci pr_info("ok: cannot locate cr4 writing call gadget\n"); 54362306a36Sopenharmony_ci return; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci direct_write_cr4 = (void *)(insn + i); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci pr_info("trying to clear SMEP with call gadget\n"); 54862306a36Sopenharmony_ci direct_write_cr4(cr4); 54962306a36Sopenharmony_ci if (native_read_cr4() & X86_CR4_SMEP) { 55062306a36Sopenharmony_ci pr_info("ok: SMEP removal was reverted\n"); 55162306a36Sopenharmony_ci } else { 55262306a36Sopenharmony_ci pr_err("FAIL: cleared SMEP not detected!\n"); 55362306a36Sopenharmony_ci cr4 |= X86_CR4_SMEP; 55462306a36Sopenharmony_ci pr_info("restoring SMEP\n"); 55562306a36Sopenharmony_ci native_write_cr4(cr4); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci#else 55862306a36Sopenharmony_ci pr_err("XFAIL: this test is x86_64-only\n"); 55962306a36Sopenharmony_ci#endif 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic void lkdtm_DOUBLE_FAULT(void) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_X86_32) && !IS_ENABLED(CONFIG_UML) 56562306a36Sopenharmony_ci /* 56662306a36Sopenharmony_ci * Trigger #DF by setting the stack limit to zero. This clobbers 56762306a36Sopenharmony_ci * a GDT TLS slot, which is okay because the current task will die 56862306a36Sopenharmony_ci * anyway due to the double fault. 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_ci struct desc_struct d = { 57162306a36Sopenharmony_ci .type = 3, /* expand-up, writable, accessed data */ 57262306a36Sopenharmony_ci .p = 1, /* present */ 57362306a36Sopenharmony_ci .d = 1, /* 32-bit */ 57462306a36Sopenharmony_ci .g = 0, /* limit in bytes */ 57562306a36Sopenharmony_ci .s = 1, /* not system */ 57662306a36Sopenharmony_ci }; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci local_irq_disable(); 57962306a36Sopenharmony_ci write_gdt_entry(get_cpu_gdt_rw(smp_processor_id()), 58062306a36Sopenharmony_ci GDT_ENTRY_TLS_MIN, &d, DESCTYPE_S); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* 58362306a36Sopenharmony_ci * Put our zero-limit segment in SS and then trigger a fault. The 58462306a36Sopenharmony_ci * 4-byte access to (%esp) will fault with #SS, and the attempt to 58562306a36Sopenharmony_ci * deliver the fault will recursively cause #SS and result in #DF. 58662306a36Sopenharmony_ci * This whole process happens while NMIs and MCEs are blocked by the 58762306a36Sopenharmony_ci * MOV SS window. This is nice because an NMI with an invalid SS 58862306a36Sopenharmony_ci * would also double-fault, resulting in the NMI or MCE being lost. 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_ci asm volatile ("movw %0, %%ss; addl $0, (%%esp)" :: 59162306a36Sopenharmony_ci "r" ((unsigned short)(GDT_ENTRY_TLS_MIN << 3))); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci pr_err("FAIL: tried to double fault but didn't die\n"); 59462306a36Sopenharmony_ci#else 59562306a36Sopenharmony_ci pr_err("XFAIL: this test is ia32-only\n"); 59662306a36Sopenharmony_ci#endif 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci#ifdef CONFIG_ARM64 60062306a36Sopenharmony_cistatic noinline void change_pac_parameters(void) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) { 60362306a36Sopenharmony_ci /* Reset the keys of current task */ 60462306a36Sopenharmony_ci ptrauth_thread_init_kernel(current); 60562306a36Sopenharmony_ci ptrauth_thread_switch_kernel(current); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci#endif 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic noinline void lkdtm_CORRUPT_PAC(void) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci#ifdef CONFIG_ARM64 61362306a36Sopenharmony_ci#define CORRUPT_PAC_ITERATE 10 61462306a36Sopenharmony_ci int i; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) 61762306a36Sopenharmony_ci pr_err("FAIL: kernel not built with CONFIG_ARM64_PTR_AUTH_KERNEL\n"); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (!system_supports_address_auth()) { 62062306a36Sopenharmony_ci pr_err("FAIL: CPU lacks pointer authentication feature\n"); 62162306a36Sopenharmony_ci return; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci pr_info("changing PAC parameters to force function return failure...\n"); 62562306a36Sopenharmony_ci /* 62662306a36Sopenharmony_ci * PAC is a hash value computed from input keys, return address and 62762306a36Sopenharmony_ci * stack pointer. As pac has fewer bits so there is a chance of 62862306a36Sopenharmony_ci * collision, so iterate few times to reduce the collision probability. 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci for (i = 0; i < CORRUPT_PAC_ITERATE; i++) 63162306a36Sopenharmony_ci change_pac_parameters(); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci pr_err("FAIL: survived PAC changes! Kernel may be unstable from here\n"); 63462306a36Sopenharmony_ci#else 63562306a36Sopenharmony_ci pr_err("XFAIL: this test is arm64-only\n"); 63662306a36Sopenharmony_ci#endif 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic struct crashtype crashtypes[] = { 64062306a36Sopenharmony_ci CRASHTYPE(PANIC), 64162306a36Sopenharmony_ci CRASHTYPE(BUG), 64262306a36Sopenharmony_ci CRASHTYPE(WARNING), 64362306a36Sopenharmony_ci CRASHTYPE(WARNING_MESSAGE), 64462306a36Sopenharmony_ci CRASHTYPE(EXCEPTION), 64562306a36Sopenharmony_ci CRASHTYPE(LOOP), 64662306a36Sopenharmony_ci CRASHTYPE(EXHAUST_STACK), 64762306a36Sopenharmony_ci CRASHTYPE(CORRUPT_STACK), 64862306a36Sopenharmony_ci CRASHTYPE(CORRUPT_STACK_STRONG), 64962306a36Sopenharmony_ci CRASHTYPE(REPORT_STACK), 65062306a36Sopenharmony_ci CRASHTYPE(REPORT_STACK_CANARY), 65162306a36Sopenharmony_ci CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE), 65262306a36Sopenharmony_ci CRASHTYPE(SOFTLOCKUP), 65362306a36Sopenharmony_ci CRASHTYPE(HARDLOCKUP), 65462306a36Sopenharmony_ci CRASHTYPE(SPINLOCKUP), 65562306a36Sopenharmony_ci CRASHTYPE(HUNG_TASK), 65662306a36Sopenharmony_ci CRASHTYPE(OVERFLOW_SIGNED), 65762306a36Sopenharmony_ci CRASHTYPE(OVERFLOW_UNSIGNED), 65862306a36Sopenharmony_ci CRASHTYPE(ARRAY_BOUNDS), 65962306a36Sopenharmony_ci CRASHTYPE(FAM_BOUNDS), 66062306a36Sopenharmony_ci CRASHTYPE(CORRUPT_LIST_ADD), 66162306a36Sopenharmony_ci CRASHTYPE(CORRUPT_LIST_DEL), 66262306a36Sopenharmony_ci CRASHTYPE(STACK_GUARD_PAGE_LEADING), 66362306a36Sopenharmony_ci CRASHTYPE(STACK_GUARD_PAGE_TRAILING), 66462306a36Sopenharmony_ci CRASHTYPE(UNSET_SMEP), 66562306a36Sopenharmony_ci CRASHTYPE(DOUBLE_FAULT), 66662306a36Sopenharmony_ci CRASHTYPE(CORRUPT_PAC), 66762306a36Sopenharmony_ci}; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistruct crashtype_category bugs_crashtypes = { 67062306a36Sopenharmony_ci .crashtypes = crashtypes, 67162306a36Sopenharmony_ci .len = ARRAY_SIZE(crashtypes), 67262306a36Sopenharmony_ci}; 673