brintos

brintos / linux-shallow public Read only

0
0
Text · 18.0 KiB · 62ba015 Raw
732 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * This is for all the tests related to logic bugs (e.g. bad dereferences,4 * bad alignment, bad loops, bad locking, bad scheduling, deep stacks, and5 * lockups) along with other things that don't fit well into existing LKDTM6 * test source files.7 */8#include "lkdtm.h"9#include <linux/cpu.h>10#include <linux/list.h>11#include <linux/sched.h>12#include <linux/sched/signal.h>13#include <linux/sched/task_stack.h>14#include <linux/slab.h>15#include <linux/stop_machine.h>16#include <linux/uaccess.h>17 18#if IS_ENABLED(CONFIG_X86_32) && !IS_ENABLED(CONFIG_UML)19#include <asm/desc.h>20#endif21 22struct lkdtm_list {23	struct list_head node;24};25 26/*27 * Make sure our attempts to over run the kernel stack doesn't trigger28 * a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we29 * recurse past the end of THREAD_SIZE by default.30 */31#if defined(CONFIG_FRAME_WARN) && (CONFIG_FRAME_WARN > 0)32#define REC_STACK_SIZE (_AC(CONFIG_FRAME_WARN, UL) / 2)33#else34#define REC_STACK_SIZE (THREAD_SIZE / 8UL)35#endif36#define REC_NUM_DEFAULT ((THREAD_SIZE / REC_STACK_SIZE) * 2)37 38static int recur_count = REC_NUM_DEFAULT;39 40static DEFINE_SPINLOCK(lock_me_up);41 42/*43 * Make sure compiler does not optimize this function or stack frame away:44 * - function marked noinline45 * - stack variables are marked volatile46 * - stack variables are written (memset()) and read (buf[..] passed as arg)47 * - function may have external effects (memzero_explicit())48 * - no tail recursion possible49 */50static int noinline recursive_loop(int remaining)51{52	volatile char buf[REC_STACK_SIZE];53	volatile int ret;54 55	memset((void *)buf, remaining & 0xFF, sizeof(buf));56	if (!remaining)57		ret = 0;58	else59		ret = recursive_loop((int)buf[remaining % sizeof(buf)] - 1);60	memzero_explicit((void *)buf, sizeof(buf));61	return ret;62}63 64/* If the depth is negative, use the default, otherwise keep parameter. */65void __init lkdtm_bugs_init(int *recur_param)66{67	if (*recur_param < 0)68		*recur_param = recur_count;69	else70		recur_count = *recur_param;71}72 73static void lkdtm_PANIC(void)74{75	panic("dumptest");76}77 78static int panic_stop_irqoff_fn(void *arg)79{80	atomic_t *v = arg;81 82	/*83	 * As stop_machine() disables interrupts, all CPUs within this function84	 * have interrupts disabled and cannot take a regular IPI.85	 *86	 * The last CPU which enters here will trigger a panic, and as all CPUs87	 * cannot take a regular IPI, we'll only be able to stop secondaries if88	 * smp_send_stop() or crash_smp_send_stop() uses an NMI.89	 */90	if (atomic_inc_return(v) == num_online_cpus())91		panic("panic stop irqoff test");92 93	for (;;)94		cpu_relax();95}96 97static void lkdtm_PANIC_STOP_IRQOFF(void)98{99	atomic_t v = ATOMIC_INIT(0);100	stop_machine(panic_stop_irqoff_fn, &v, cpu_online_mask);101}102 103static void lkdtm_BUG(void)104{105	BUG();106}107 108static int warn_counter;109 110static void lkdtm_WARNING(void)111{112	WARN_ON(++warn_counter);113}114 115static void lkdtm_WARNING_MESSAGE(void)116{117	WARN(1, "Warning message trigger count: %d\n", ++warn_counter);118}119 120static void lkdtm_EXCEPTION(void)121{122	*((volatile int *) 0) = 0;123}124 125static void lkdtm_LOOP(void)126{127	for (;;)128		;129}130 131static void lkdtm_EXHAUST_STACK(void)132{133	pr_info("Calling function with %lu frame size to depth %d ...\n",134		REC_STACK_SIZE, recur_count);135	recursive_loop(recur_count);136	pr_info("FAIL: survived without exhausting stack?!\n");137}138 139static noinline void __lkdtm_CORRUPT_STACK(void *stack)140{141	memset(stack, '\xff', 64);142}143 144/* This should trip the stack canary, not corrupt the return address. */145static noinline void lkdtm_CORRUPT_STACK(void)146{147	/* Use default char array length that triggers stack protection. */148	char data[8] __aligned(sizeof(void *));149 150	pr_info("Corrupting stack containing char array ...\n");151	__lkdtm_CORRUPT_STACK((void *)&data);152}153 154/* Same as above but will only get a canary with -fstack-protector-strong */155static noinline void lkdtm_CORRUPT_STACK_STRONG(void)156{157	union {158		unsigned short shorts[4];159		unsigned long *ptr;160	} data __aligned(sizeof(void *));161 162	pr_info("Corrupting stack containing union ...\n");163	__lkdtm_CORRUPT_STACK((void *)&data);164}165 166static pid_t stack_pid;167static unsigned long stack_addr;168 169static void lkdtm_REPORT_STACK(void)170{171	volatile uintptr_t magic;172	pid_t pid = task_pid_nr(current);173 174	if (pid != stack_pid) {175		pr_info("Starting stack offset tracking for pid %d\n", pid);176		stack_pid = pid;177		stack_addr = (uintptr_t)&magic;178	}179 180	pr_info("Stack offset: %d\n", (int)(stack_addr - (uintptr_t)&magic));181}182 183static pid_t stack_canary_pid;184static unsigned long stack_canary;185static unsigned long stack_canary_offset;186 187static noinline void __lkdtm_REPORT_STACK_CANARY(void *stack)188{189	int i = 0;190	pid_t pid = task_pid_nr(current);191	unsigned long *canary = (unsigned long *)stack;192	unsigned long current_offset = 0, init_offset = 0;193 194	/* Do our best to find the canary in a 16 word window ... */195	for (i = 1; i < 16; i++) {196		canary = (unsigned long *)stack + i;197#ifdef CONFIG_STACKPROTECTOR198		if (*canary == current->stack_canary)199			current_offset = i;200		if (*canary == init_task.stack_canary)201			init_offset = i;202#endif203	}204 205	if (current_offset == 0) {206		/*207		 * If the canary doesn't match what's in the task_struct,208		 * we're either using a global canary or the stack frame209		 * layout changed.210		 */211		if (init_offset != 0) {212			pr_err("FAIL: global stack canary found at offset %ld (canary for pid %d matches init_task's)!\n",213			       init_offset, pid);214		} else {215			pr_warn("FAIL: did not correctly locate stack canary :(\n");216			pr_expected_config(CONFIG_STACKPROTECTOR);217		}218 219		return;220	} else if (init_offset != 0) {221		pr_warn("WARNING: found both current and init_task canaries nearby?!\n");222	}223 224	canary = (unsigned long *)stack + current_offset;225	if (stack_canary_pid == 0) {226		stack_canary = *canary;227		stack_canary_pid = pid;228		stack_canary_offset = current_offset;229		pr_info("Recorded stack canary for pid %d at offset %ld\n",230			stack_canary_pid, stack_canary_offset);231	} else if (pid == stack_canary_pid) {232		pr_warn("ERROR: saw pid %d again -- please use a new pid\n", pid);233	} else {234		if (current_offset != stack_canary_offset) {235			pr_warn("ERROR: canary offset changed from %ld to %ld!?\n",236				stack_canary_offset, current_offset);237			return;238		}239 240		if (*canary == stack_canary) {241			pr_warn("FAIL: canary identical for pid %d and pid %d at offset %ld!\n",242				stack_canary_pid, pid, current_offset);243		} else {244			pr_info("ok: stack canaries differ between pid %d and pid %d at offset %ld.\n",245				stack_canary_pid, pid, current_offset);246			/* Reset the test. */247			stack_canary_pid = 0;248		}249	}250}251 252static void lkdtm_REPORT_STACK_CANARY(void)253{254	/* Use default char array length that triggers stack protection. */255	char data[8] __aligned(sizeof(void *)) = { };256 257	__lkdtm_REPORT_STACK_CANARY((void *)&data);258}259 260static void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void)261{262	static u8 data[5] __attribute__((aligned(4))) = {1, 2, 3, 4, 5};263	u32 *p;264	u32 val = 0x12345678;265 266	p = (u32 *)(data + 1);267	if (*p == 0)268		val = 0x87654321;269	*p = val;270 271	if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))272		pr_err("XFAIL: arch has CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS\n");273}274 275static void lkdtm_SOFTLOCKUP(void)276{277	preempt_disable();278	for (;;)279		cpu_relax();280}281 282static void lkdtm_HARDLOCKUP(void)283{284	local_irq_disable();285	for (;;)286		cpu_relax();287}288 289static void __lkdtm_SMP_CALL_LOCKUP(void *unused)290{291	for (;;)292		cpu_relax();293}294 295static void lkdtm_SMP_CALL_LOCKUP(void)296{297	unsigned int cpu, target;298 299	cpus_read_lock();300 301	cpu = get_cpu();302	target = cpumask_any_but(cpu_online_mask, cpu);303 304	if (target >= nr_cpu_ids) {305		pr_err("FAIL: no other online CPUs\n");306		goto out_put_cpus;307	}308 309	smp_call_function_single(target, __lkdtm_SMP_CALL_LOCKUP, NULL, 1);310 311	pr_err("FAIL: did not hang\n");312 313out_put_cpus:314	put_cpu();315	cpus_read_unlock();316}317 318static void lkdtm_SPINLOCKUP(void)319{320	/* Must be called twice to trigger. */321	spin_lock(&lock_me_up);322	/* Let sparse know we intended to exit holding the lock. */323	__release(&lock_me_up);324}325 326static void __noreturn lkdtm_HUNG_TASK(void)327{328	set_current_state(TASK_UNINTERRUPTIBLE);329	schedule();330	BUG();331}332 333static volatile unsigned int huge = INT_MAX - 2;334static volatile unsigned int ignored;335 336static void lkdtm_OVERFLOW_SIGNED(void)337{338	int value;339 340	value = huge;341	pr_info("Normal signed addition ...\n");342	value += 1;343	ignored = value;344 345	pr_info("Overflowing signed addition ...\n");346	value += 4;347	ignored = value;348}349 350 351static void lkdtm_OVERFLOW_UNSIGNED(void)352{353	unsigned int value;354 355	value = huge;356	pr_info("Normal unsigned addition ...\n");357	value += 1;358	ignored = value;359 360	pr_info("Overflowing unsigned addition ...\n");361	value += 4;362	ignored = value;363}364 365/* Intentionally using unannotated flex array definition. */366struct array_bounds_flex_array {367	int one;368	int two;369	char data[];370};371 372struct array_bounds {373	int one;374	int two;375	char data[8];376	int three;377};378 379static void lkdtm_ARRAY_BOUNDS(void)380{381	struct array_bounds_flex_array *not_checked;382	struct array_bounds *checked;383	volatile int i;384 385	not_checked = kmalloc(sizeof(*not_checked) * 2, GFP_KERNEL);386	checked = kmalloc(sizeof(*checked) * 2, GFP_KERNEL);387	if (!not_checked || !checked) {388		kfree(not_checked);389		kfree(checked);390		return;391	}392 393	pr_info("Array access within bounds ...\n");394	/* For both, touch all bytes in the actual member size. */395	for (i = 0; i < sizeof(checked->data); i++)396		checked->data[i] = 'A';397	/*398	 * For the uninstrumented flex array member, also touch 1 byte399	 * beyond to verify it is correctly uninstrumented.400	 */401	for (i = 0; i < 2; i++)402		not_checked->data[i] = 'A';403 404	pr_info("Array access beyond bounds ...\n");405	for (i = 0; i < sizeof(checked->data) + 1; i++)406		checked->data[i] = 'B';407 408	kfree(not_checked);409	kfree(checked);410	pr_err("FAIL: survived array bounds overflow!\n");411	if (IS_ENABLED(CONFIG_UBSAN_BOUNDS))412		pr_expected_config(CONFIG_UBSAN_TRAP);413	else414		pr_expected_config(CONFIG_UBSAN_BOUNDS);415}416 417struct lkdtm_annotated {418	unsigned long flags;419	int count;420	int array[] __counted_by(count);421};422 423static volatile int fam_count = 4;424 425static void lkdtm_FAM_BOUNDS(void)426{427	struct lkdtm_annotated *inst;428 429	inst = kzalloc(struct_size(inst, array, fam_count + 1), GFP_KERNEL);430	if (!inst) {431		pr_err("FAIL: could not allocate test struct!\n");432		return;433	}434 435	inst->count = fam_count;436	pr_info("Array access within bounds ...\n");437	inst->array[1] = fam_count;438	ignored = inst->array[1];439 440	pr_info("Array access beyond bounds ...\n");441	inst->array[fam_count] = fam_count;442	ignored = inst->array[fam_count];443 444	kfree(inst);445 446	pr_err("FAIL: survived access of invalid flexible array member index!\n");447 448	if (!__has_attribute(__counted_by__))449		pr_warn("This is expected since this %s was built with a compiler that does not support __counted_by\n",450			lkdtm_kernel_info);451	else if (IS_ENABLED(CONFIG_UBSAN_BOUNDS))452		pr_expected_config(CONFIG_UBSAN_TRAP);453	else454		pr_expected_config(CONFIG_UBSAN_BOUNDS);455}456 457static void lkdtm_CORRUPT_LIST_ADD(void)458{459	/*460	 * Initially, an empty list via LIST_HEAD:461	 *	test_head.next = &test_head462	 *	test_head.prev = &test_head463	 */464	LIST_HEAD(test_head);465	struct lkdtm_list good, bad;466	void *target[2] = { };467	void *redirection = &target;468 469	pr_info("attempting good list addition\n");470 471	/*472	 * Adding to the list performs these actions:473	 *	test_head.next->prev = &good.node474	 *	good.node.next = test_head.next475	 *	good.node.prev = test_head476	 *	test_head.next = good.node477	 */478	list_add(&good.node, &test_head);479 480	pr_info("attempting corrupted list addition\n");481	/*482	 * In simulating this "write what where" primitive, the "what" is483	 * the address of &bad.node, and the "where" is the address held484	 * by "redirection".485	 */486	test_head.next = redirection;487	list_add(&bad.node, &test_head);488 489	if (target[0] == NULL && target[1] == NULL)490		pr_err("Overwrite did not happen, but no BUG?!\n");491	else {492		pr_err("list_add() corruption not detected!\n");493		pr_expected_config(CONFIG_LIST_HARDENED);494	}495}496 497static void lkdtm_CORRUPT_LIST_DEL(void)498{499	LIST_HEAD(test_head);500	struct lkdtm_list item;501	void *target[2] = { };502	void *redirection = &target;503 504	list_add(&item.node, &test_head);505 506	pr_info("attempting good list removal\n");507	list_del(&item.node);508 509	pr_info("attempting corrupted list removal\n");510	list_add(&item.node, &test_head);511 512	/* As with the list_add() test above, this corrupts "next". */513	item.node.next = redirection;514	list_del(&item.node);515 516	if (target[0] == NULL && target[1] == NULL)517		pr_err("Overwrite did not happen, but no BUG?!\n");518	else {519		pr_err("list_del() corruption not detected!\n");520		pr_expected_config(CONFIG_LIST_HARDENED);521	}522}523 524/* Test that VMAP_STACK is actually allocating with a leading guard page */525static void lkdtm_STACK_GUARD_PAGE_LEADING(void)526{527	const unsigned char *stack = task_stack_page(current);528	const unsigned char *ptr = stack - 1;529	volatile unsigned char byte;530 531	pr_info("attempting bad read from page below current stack\n");532 533	byte = *ptr;534 535	pr_err("FAIL: accessed page before stack! (byte: %x)\n", byte);536}537 538/* Test that VMAP_STACK is actually allocating with a trailing guard page */539static void lkdtm_STACK_GUARD_PAGE_TRAILING(void)540{541	const unsigned char *stack = task_stack_page(current);542	const unsigned char *ptr = stack + THREAD_SIZE;543	volatile unsigned char byte;544 545	pr_info("attempting bad read from page above current stack\n");546 547	byte = *ptr;548 549	pr_err("FAIL: accessed page after stack! (byte: %x)\n", byte);550}551 552static void lkdtm_UNSET_SMEP(void)553{554#if IS_ENABLED(CONFIG_X86_64) && !IS_ENABLED(CONFIG_UML)555#define MOV_CR4_DEPTH	64556	void (*direct_write_cr4)(unsigned long val);557	unsigned char *insn;558	unsigned long cr4;559	int i;560 561	cr4 = native_read_cr4();562 563	if ((cr4 & X86_CR4_SMEP) != X86_CR4_SMEP) {564		pr_err("FAIL: SMEP not in use\n");565		return;566	}567	cr4 &= ~(X86_CR4_SMEP);568 569	pr_info("trying to clear SMEP normally\n");570	native_write_cr4(cr4);571	if (cr4 == native_read_cr4()) {572		pr_err("FAIL: pinning SMEP failed!\n");573		cr4 |= X86_CR4_SMEP;574		pr_info("restoring SMEP\n");575		native_write_cr4(cr4);576		return;577	}578	pr_info("ok: SMEP did not get cleared\n");579 580	/*581	 * To test the post-write pinning verification we need to call582	 * directly into the middle of native_write_cr4() where the583	 * cr4 write happens, skipping any pinning. This searches for584	 * the cr4 writing instruction.585	 */586	insn = (unsigned char *)native_write_cr4;587	OPTIMIZER_HIDE_VAR(insn);588	for (i = 0; i < MOV_CR4_DEPTH; i++) {589		/* mov %rdi, %cr4 */590		if (insn[i] == 0x0f && insn[i+1] == 0x22 && insn[i+2] == 0xe7)591			break;592		/* mov %rdi,%rax; mov %rax, %cr4 */593		if (insn[i]   == 0x48 && insn[i+1] == 0x89 &&594		    insn[i+2] == 0xf8 && insn[i+3] == 0x0f &&595		    insn[i+4] == 0x22 && insn[i+5] == 0xe0)596			break;597	}598	if (i >= MOV_CR4_DEPTH) {599		pr_info("ok: cannot locate cr4 writing call gadget\n");600		return;601	}602	direct_write_cr4 = (void *)(insn + i);603 604	pr_info("trying to clear SMEP with call gadget\n");605	direct_write_cr4(cr4);606	if (native_read_cr4() & X86_CR4_SMEP) {607		pr_info("ok: SMEP removal was reverted\n");608	} else {609		pr_err("FAIL: cleared SMEP not detected!\n");610		cr4 |= X86_CR4_SMEP;611		pr_info("restoring SMEP\n");612		native_write_cr4(cr4);613	}614#else615	pr_err("XFAIL: this test is x86_64-only\n");616#endif617}618 619static void lkdtm_DOUBLE_FAULT(void)620{621#if IS_ENABLED(CONFIG_X86_32) && !IS_ENABLED(CONFIG_UML)622	/*623	 * Trigger #DF by setting the stack limit to zero.  This clobbers624	 * a GDT TLS slot, which is okay because the current task will die625	 * anyway due to the double fault.626	 */627	struct desc_struct d = {628		.type = 3,	/* expand-up, writable, accessed data */629		.p = 1,		/* present */630		.d = 1,		/* 32-bit */631		.g = 0,		/* limit in bytes */632		.s = 1,		/* not system */633	};634 635	local_irq_disable();636	write_gdt_entry(get_cpu_gdt_rw(smp_processor_id()),637			GDT_ENTRY_TLS_MIN, &d, DESCTYPE_S);638 639	/*640	 * Put our zero-limit segment in SS and then trigger a fault.  The641	 * 4-byte access to (%esp) will fault with #SS, and the attempt to642	 * deliver the fault will recursively cause #SS and result in #DF.643	 * This whole process happens while NMIs and MCEs are blocked by the644	 * MOV SS window.  This is nice because an NMI with an invalid SS645	 * would also double-fault, resulting in the NMI or MCE being lost.646	 */647	asm volatile ("movw %0, %%ss; addl $0, (%%esp)" ::648		      "r" ((unsigned short)(GDT_ENTRY_TLS_MIN << 3)));649 650	pr_err("FAIL: tried to double fault but didn't die\n");651#else652	pr_err("XFAIL: this test is ia32-only\n");653#endif654}655 656#ifdef CONFIG_ARM64657static noinline void change_pac_parameters(void)658{659	if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) {660		/* Reset the keys of current task */661		ptrauth_thread_init_kernel(current);662		ptrauth_thread_switch_kernel(current);663	}664}665#endif666 667static noinline void lkdtm_CORRUPT_PAC(void)668{669#ifdef CONFIG_ARM64670#define CORRUPT_PAC_ITERATE	10671	int i;672 673	if (!IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL))674		pr_err("FAIL: kernel not built with CONFIG_ARM64_PTR_AUTH_KERNEL\n");675 676	if (!system_supports_address_auth()) {677		pr_err("FAIL: CPU lacks pointer authentication feature\n");678		return;679	}680 681	pr_info("changing PAC parameters to force function return failure...\n");682	/*683	 * PAC is a hash value computed from input keys, return address and684	 * stack pointer. As pac has fewer bits so there is a chance of685	 * collision, so iterate few times to reduce the collision probability.686	 */687	for (i = 0; i < CORRUPT_PAC_ITERATE; i++)688		change_pac_parameters();689 690	pr_err("FAIL: survived PAC changes! Kernel may be unstable from here\n");691#else692	pr_err("XFAIL: this test is arm64-only\n");693#endif694}695 696static struct crashtype crashtypes[] = {697	CRASHTYPE(PANIC),698	CRASHTYPE(PANIC_STOP_IRQOFF),699	CRASHTYPE(BUG),700	CRASHTYPE(WARNING),701	CRASHTYPE(WARNING_MESSAGE),702	CRASHTYPE(EXCEPTION),703	CRASHTYPE(LOOP),704	CRASHTYPE(EXHAUST_STACK),705	CRASHTYPE(CORRUPT_STACK),706	CRASHTYPE(CORRUPT_STACK_STRONG),707	CRASHTYPE(REPORT_STACK),708	CRASHTYPE(REPORT_STACK_CANARY),709	CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE),710	CRASHTYPE(SOFTLOCKUP),711	CRASHTYPE(HARDLOCKUP),712	CRASHTYPE(SMP_CALL_LOCKUP),713	CRASHTYPE(SPINLOCKUP),714	CRASHTYPE(HUNG_TASK),715	CRASHTYPE(OVERFLOW_SIGNED),716	CRASHTYPE(OVERFLOW_UNSIGNED),717	CRASHTYPE(ARRAY_BOUNDS),718	CRASHTYPE(FAM_BOUNDS),719	CRASHTYPE(CORRUPT_LIST_ADD),720	CRASHTYPE(CORRUPT_LIST_DEL),721	CRASHTYPE(STACK_GUARD_PAGE_LEADING),722	CRASHTYPE(STACK_GUARD_PAGE_TRAILING),723	CRASHTYPE(UNSET_SMEP),724	CRASHTYPE(DOUBLE_FAULT),725	CRASHTYPE(CORRUPT_PAC),726};727 728struct crashtype_category bugs_crashtypes = {729	.crashtypes = crashtypes,730	.len	    = ARRAY_SIZE(crashtypes),731};732