162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * This is for all the tests relating directly to Control Flow Integrity.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include "lkdtm.h"
662306a36Sopenharmony_ci#include <asm/page.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_cistatic int called_count;
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/* Function taking one argument, without a return value. */
1162306a36Sopenharmony_cistatic noinline void lkdtm_increment_void(int *counter)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	(*counter)++;
1462306a36Sopenharmony_ci}
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* Function taking one argument, returning int. */
1762306a36Sopenharmony_cistatic noinline int lkdtm_increment_int(int *counter)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	(*counter)++;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	return *counter;
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* Don't allow the compiler to inline the calls. */
2562306a36Sopenharmony_cistatic noinline void lkdtm_indirect_call(void (*func)(int *))
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	func(&called_count);
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * This tries to call an indirect function with a mismatched prototype.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_cistatic void lkdtm_CFI_FORWARD_PROTO(void)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	/*
3662306a36Sopenharmony_ci	 * Matches lkdtm_increment_void()'s prototype, but not
3762306a36Sopenharmony_ci	 * lkdtm_increment_int()'s prototype.
3862306a36Sopenharmony_ci	 */
3962306a36Sopenharmony_ci	pr_info("Calling matched prototype ...\n");
4062306a36Sopenharmony_ci	lkdtm_indirect_call(lkdtm_increment_void);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	pr_info("Calling mismatched prototype ...\n");
4362306a36Sopenharmony_ci	lkdtm_indirect_call((void *)lkdtm_increment_int);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	pr_err("FAIL: survived mismatched prototype function call!\n");
4662306a36Sopenharmony_ci	pr_expected_config(CONFIG_CFI_CLANG);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/*
5062306a36Sopenharmony_ci * This can stay local to LKDTM, as there should not be a production reason
5162306a36Sopenharmony_ci * to disable PAC && SCS.
5262306a36Sopenharmony_ci */
5362306a36Sopenharmony_ci#ifdef CONFIG_ARM64_PTR_AUTH_KERNEL
5462306a36Sopenharmony_ci# ifdef CONFIG_ARM64_BTI_KERNEL
5562306a36Sopenharmony_ci#  define __no_pac             "branch-protection=bti"
5662306a36Sopenharmony_ci# else
5762306a36Sopenharmony_ci#  ifdef CONFIG_CC_HAS_BRANCH_PROT_PAC_RET
5862306a36Sopenharmony_ci#   define __no_pac            "branch-protection=none"
5962306a36Sopenharmony_ci#  else
6062306a36Sopenharmony_ci#   define __no_pac            "sign-return-address=none"
6162306a36Sopenharmony_ci#  endif
6262306a36Sopenharmony_ci# endif
6362306a36Sopenharmony_ci# define __no_ret_protection   __noscs __attribute__((__target__(__no_pac)))
6462306a36Sopenharmony_ci#else
6562306a36Sopenharmony_ci# define __no_ret_protection   __noscs
6662306a36Sopenharmony_ci#endif
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define no_pac_addr(addr)      \
6962306a36Sopenharmony_ci	((__force __typeof__(addr))((uintptr_t)(addr) | PAGE_OFFSET))
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* The ultimate ROP gadget. */
7262306a36Sopenharmony_cistatic noinline __no_ret_protection
7362306a36Sopenharmony_civoid set_return_addr_unchecked(unsigned long *expected, unsigned long *addr)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	/* Use of volatile is to make sure final write isn't seen as a dead store. */
7662306a36Sopenharmony_ci	unsigned long * volatile *ret_addr = (unsigned long **)__builtin_frame_address(0) + 1;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* Make sure we've found the right place on the stack before writing it. */
7962306a36Sopenharmony_ci	if (no_pac_addr(*ret_addr) == expected)
8062306a36Sopenharmony_ci		*ret_addr = (addr);
8162306a36Sopenharmony_ci	else
8262306a36Sopenharmony_ci		/* Check architecture, stack layout, or compiler behavior... */
8362306a36Sopenharmony_ci		pr_warn("Eek: return address mismatch! %px != %px\n",
8462306a36Sopenharmony_ci			*ret_addr, addr);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic noinline
8862306a36Sopenharmony_civoid set_return_addr(unsigned long *expected, unsigned long *addr)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	/* Use of volatile is to make sure final write isn't seen as a dead store. */
9162306a36Sopenharmony_ci	unsigned long * volatile *ret_addr = (unsigned long **)__builtin_frame_address(0) + 1;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* Make sure we've found the right place on the stack before writing it. */
9462306a36Sopenharmony_ci	if (no_pac_addr(*ret_addr) == expected)
9562306a36Sopenharmony_ci		*ret_addr = (addr);
9662306a36Sopenharmony_ci	else
9762306a36Sopenharmony_ci		/* Check architecture, stack layout, or compiler behavior... */
9862306a36Sopenharmony_ci		pr_warn("Eek: return address mismatch! %px != %px\n",
9962306a36Sopenharmony_ci			*ret_addr, addr);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic volatile int force_check;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void lkdtm_CFI_BACKWARD(void)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	/* Use calculated gotos to keep labels addressable. */
10762306a36Sopenharmony_ci	void *labels[] = { NULL, &&normal, &&redirected, &&check_normal, &&check_redirected };
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	pr_info("Attempting unchecked stack return address redirection ...\n");
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Always false */
11262306a36Sopenharmony_ci	if (force_check) {
11362306a36Sopenharmony_ci		/*
11462306a36Sopenharmony_ci		 * Prepare to call with NULLs to avoid parameters being treated as
11562306a36Sopenharmony_ci		 * constants in -02.
11662306a36Sopenharmony_ci		 */
11762306a36Sopenharmony_ci		set_return_addr_unchecked(NULL, NULL);
11862306a36Sopenharmony_ci		set_return_addr(NULL, NULL);
11962306a36Sopenharmony_ci		if (force_check)
12062306a36Sopenharmony_ci			goto *labels[1];
12162306a36Sopenharmony_ci		if (force_check)
12262306a36Sopenharmony_ci			goto *labels[2];
12362306a36Sopenharmony_ci		if (force_check)
12462306a36Sopenharmony_ci			goto *labels[3];
12562306a36Sopenharmony_ci		if (force_check)
12662306a36Sopenharmony_ci			goto *labels[4];
12762306a36Sopenharmony_ci		return;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/*
13162306a36Sopenharmony_ci	 * Use fallthrough switch case to keep basic block ordering between
13262306a36Sopenharmony_ci	 * set_return_addr*() and the label after it.
13362306a36Sopenharmony_ci	 */
13462306a36Sopenharmony_ci	switch (force_check) {
13562306a36Sopenharmony_ci	case 0:
13662306a36Sopenharmony_ci		set_return_addr_unchecked(&&normal, &&redirected);
13762306a36Sopenharmony_ci		fallthrough;
13862306a36Sopenharmony_ci	case 1:
13962306a36Sopenharmony_cinormal:
14062306a36Sopenharmony_ci		/* Always true */
14162306a36Sopenharmony_ci		if (!force_check) {
14262306a36Sopenharmony_ci			pr_err("FAIL: stack return address manipulation failed!\n");
14362306a36Sopenharmony_ci			/* If we can't redirect "normally", we can't test mitigations. */
14462306a36Sopenharmony_ci			return;
14562306a36Sopenharmony_ci		}
14662306a36Sopenharmony_ci		break;
14762306a36Sopenharmony_ci	default:
14862306a36Sopenharmony_ciredirected:
14962306a36Sopenharmony_ci		pr_info("ok: redirected stack return address.\n");
15062306a36Sopenharmony_ci		break;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	pr_info("Attempting checked stack return address redirection ...\n");
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	switch (force_check) {
15662306a36Sopenharmony_ci	case 0:
15762306a36Sopenharmony_ci		set_return_addr(&&check_normal, &&check_redirected);
15862306a36Sopenharmony_ci		fallthrough;
15962306a36Sopenharmony_ci	case 1:
16062306a36Sopenharmony_cicheck_normal:
16162306a36Sopenharmony_ci		/* Always true */
16262306a36Sopenharmony_ci		if (!force_check) {
16362306a36Sopenharmony_ci			pr_info("ok: control flow unchanged.\n");
16462306a36Sopenharmony_ci			return;
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cicheck_redirected:
16862306a36Sopenharmony_ci		pr_err("FAIL: stack return address was redirected!\n");
16962306a36Sopenharmony_ci		break;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) {
17362306a36Sopenharmony_ci		pr_expected_config(CONFIG_ARM64_PTR_AUTH_KERNEL);
17462306a36Sopenharmony_ci		return;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_SHADOW_CALL_STACK)) {
17762306a36Sopenharmony_ci		pr_expected_config(CONFIG_SHADOW_CALL_STACK);
17862306a36Sopenharmony_ci		return;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci	pr_warn("This is probably expected, since this %s was built *without* %s=y nor %s=y\n",
18162306a36Sopenharmony_ci		lkdtm_kernel_info,
18262306a36Sopenharmony_ci		"CONFIG_ARM64_PTR_AUTH_KERNEL", "CONFIG_SHADOW_CALL_STACK");
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic struct crashtype crashtypes[] = {
18662306a36Sopenharmony_ci	CRASHTYPE(CFI_FORWARD_PROTO),
18762306a36Sopenharmony_ci	CRASHTYPE(CFI_BACKWARD),
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistruct crashtype_category cfi_crashtypes = {
19162306a36Sopenharmony_ci	.crashtypes = crashtypes,
19262306a36Sopenharmony_ci	.len	    = ARRAY_SIZE(crashtypes),
19362306a36Sopenharmony_ci};
194