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 = &target;
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 = &target;
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