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