xref: /openbmc/linux/drivers/misc/lkdtm/cfi.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
1b0eb93cfSKees Cook // SPDX-License-Identifier: GPL-2.0
2b0eb93cfSKees Cook /*
3b0eb93cfSKees Cook  * This is for all the tests relating directly to Control Flow Integrity.
4b0eb93cfSKees Cook  */
5b0eb93cfSKees Cook #include "lkdtm.h"
62e53b877SKees Cook #include <asm/page.h>
7b0eb93cfSKees Cook 
8b0eb93cfSKees Cook static int called_count;
9b0eb93cfSKees Cook 
10b0eb93cfSKees Cook /* Function taking one argument, without a return value. */
lkdtm_increment_void(int * counter)11b0eb93cfSKees Cook static noinline void lkdtm_increment_void(int *counter)
12b0eb93cfSKees Cook {
13b0eb93cfSKees Cook 	(*counter)++;
14b0eb93cfSKees Cook }
15b0eb93cfSKees Cook 
16b0eb93cfSKees Cook /* Function taking one argument, returning int. */
lkdtm_increment_int(int * counter)17b0eb93cfSKees Cook static noinline int lkdtm_increment_int(int *counter)
18b0eb93cfSKees Cook {
19b0eb93cfSKees Cook 	(*counter)++;
20b0eb93cfSKees Cook 
21b0eb93cfSKees Cook 	return *counter;
22b0eb93cfSKees Cook }
23cf90d038SSami Tolvanen 
24cf90d038SSami Tolvanen /* Don't allow the compiler to inline the calls. */
lkdtm_indirect_call(void (* func)(int *))25cf90d038SSami Tolvanen static noinline void lkdtm_indirect_call(void (*func)(int *))
26cf90d038SSami Tolvanen {
27cf90d038SSami Tolvanen 	func(&called_count);
28cf90d038SSami Tolvanen }
29cf90d038SSami Tolvanen 
30b0eb93cfSKees Cook /*
31b0eb93cfSKees Cook  * This tries to call an indirect function with a mismatched prototype.
32b0eb93cfSKees Cook  */
lkdtm_CFI_FORWARD_PROTO(void)3373f62e60SKees Cook static void lkdtm_CFI_FORWARD_PROTO(void)
34b0eb93cfSKees Cook {
35b0eb93cfSKees Cook 	/*
36b0eb93cfSKees Cook 	 * Matches lkdtm_increment_void()'s prototype, but not
37b0eb93cfSKees Cook 	 * lkdtm_increment_int()'s prototype.
38b0eb93cfSKees Cook 	 */
39b0eb93cfSKees Cook 	pr_info("Calling matched prototype ...\n");
40cf90d038SSami Tolvanen 	lkdtm_indirect_call(lkdtm_increment_void);
41b0eb93cfSKees Cook 
42b0eb93cfSKees Cook 	pr_info("Calling mismatched prototype ...\n");
43cf90d038SSami Tolvanen 	lkdtm_indirect_call((void *)lkdtm_increment_int);
44b0eb93cfSKees Cook 
455b777131SKees Cook 	pr_err("FAIL: survived mismatched prototype function call!\n");
465b777131SKees Cook 	pr_expected_config(CONFIG_CFI_CLANG);
47b0eb93cfSKees Cook }
4873f62e60SKees Cook 
492e53b877SKees Cook /*
502e53b877SKees Cook  * This can stay local to LKDTM, as there should not be a production reason
512e53b877SKees Cook  * to disable PAC && SCS.
522e53b877SKees Cook  */
532e53b877SKees Cook #ifdef CONFIG_ARM64_PTR_AUTH_KERNEL
542e53b877SKees Cook # ifdef CONFIG_ARM64_BTI_KERNEL
552e53b877SKees Cook #  define __no_pac             "branch-protection=bti"
562e53b877SKees Cook # else
57*f68022aeSKristina Martsenko #  ifdef CONFIG_CC_HAS_BRANCH_PROT_PAC_RET
582e53b877SKees Cook #   define __no_pac            "branch-protection=none"
59*f68022aeSKristina Martsenko #  else
60*f68022aeSKristina Martsenko #   define __no_pac            "sign-return-address=none"
61*f68022aeSKristina Martsenko #  endif
622e53b877SKees Cook # endif
632e53b877SKees Cook # define __no_ret_protection   __noscs __attribute__((__target__(__no_pac)))
642e53b877SKees Cook #else
652e53b877SKees Cook # define __no_ret_protection   __noscs
662e53b877SKees Cook #endif
672e53b877SKees Cook 
682e53b877SKees Cook #define no_pac_addr(addr)      \
6977acbdc0SKees Cook 	((__force __typeof__(addr))((uintptr_t)(addr) | PAGE_OFFSET))
702e53b877SKees Cook 
712e53b877SKees Cook /* The ultimate ROP gadget. */
722e53b877SKees Cook static noinline __no_ret_protection
set_return_addr_unchecked(unsigned long * expected,unsigned long * addr)732e53b877SKees Cook void set_return_addr_unchecked(unsigned long *expected, unsigned long *addr)
742e53b877SKees Cook {
752e53b877SKees Cook 	/* Use of volatile is to make sure final write isn't seen as a dead store. */
762e53b877SKees Cook 	unsigned long * volatile *ret_addr = (unsigned long **)__builtin_frame_address(0) + 1;
772e53b877SKees Cook 
782e53b877SKees Cook 	/* Make sure we've found the right place on the stack before writing it. */
792e53b877SKees Cook 	if (no_pac_addr(*ret_addr) == expected)
802e53b877SKees Cook 		*ret_addr = (addr);
812e53b877SKees Cook 	else
822e53b877SKees Cook 		/* Check architecture, stack layout, or compiler behavior... */
832e53b877SKees Cook 		pr_warn("Eek: return address mismatch! %px != %px\n",
842e53b877SKees Cook 			*ret_addr, addr);
852e53b877SKees Cook }
862e53b877SKees Cook 
872e53b877SKees Cook static noinline
set_return_addr(unsigned long * expected,unsigned long * addr)882e53b877SKees Cook void set_return_addr(unsigned long *expected, unsigned long *addr)
892e53b877SKees Cook {
902e53b877SKees Cook 	/* Use of volatile is to make sure final write isn't seen as a dead store. */
912e53b877SKees Cook 	unsigned long * volatile *ret_addr = (unsigned long **)__builtin_frame_address(0) + 1;
922e53b877SKees Cook 
932e53b877SKees Cook 	/* Make sure we've found the right place on the stack before writing it. */
942e53b877SKees Cook 	if (no_pac_addr(*ret_addr) == expected)
952e53b877SKees Cook 		*ret_addr = (addr);
962e53b877SKees Cook 	else
972e53b877SKees Cook 		/* Check architecture, stack layout, or compiler behavior... */
982e53b877SKees Cook 		pr_warn("Eek: return address mismatch! %px != %px\n",
992e53b877SKees Cook 			*ret_addr, addr);
1002e53b877SKees Cook }
1012e53b877SKees Cook 
1022e53b877SKees Cook static volatile int force_check;
1032e53b877SKees Cook 
lkdtm_CFI_BACKWARD(void)1042e53b877SKees Cook static void lkdtm_CFI_BACKWARD(void)
1052e53b877SKees Cook {
1062e53b877SKees Cook 	/* Use calculated gotos to keep labels addressable. */
1075afbfa8cSColin Ian King 	void *labels[] = { NULL, &&normal, &&redirected, &&check_normal, &&check_redirected };
1082e53b877SKees Cook 
1092e53b877SKees Cook 	pr_info("Attempting unchecked stack return address redirection ...\n");
1102e53b877SKees Cook 
1112e53b877SKees Cook 	/* Always false */
1122e53b877SKees Cook 	if (force_check) {
1132e53b877SKees Cook 		/*
1142e53b877SKees Cook 		 * Prepare to call with NULLs to avoid parameters being treated as
1152e53b877SKees Cook 		 * constants in -02.
1162e53b877SKees Cook 		 */
1172e53b877SKees Cook 		set_return_addr_unchecked(NULL, NULL);
1182e53b877SKees Cook 		set_return_addr(NULL, NULL);
1192e53b877SKees Cook 		if (force_check)
1202e53b877SKees Cook 			goto *labels[1];
1212e53b877SKees Cook 		if (force_check)
1222e53b877SKees Cook 			goto *labels[2];
1232e53b877SKees Cook 		if (force_check)
1242e53b877SKees Cook 			goto *labels[3];
1252e53b877SKees Cook 		if (force_check)
1262e53b877SKees Cook 			goto *labels[4];
1272e53b877SKees Cook 		return;
1282e53b877SKees Cook 	}
1292e53b877SKees Cook 
1302e53b877SKees Cook 	/*
1312e53b877SKees Cook 	 * Use fallthrough switch case to keep basic block ordering between
1322e53b877SKees Cook 	 * set_return_addr*() and the label after it.
1332e53b877SKees Cook 	 */
1342e53b877SKees Cook 	switch (force_check) {
1352e53b877SKees Cook 	case 0:
1362e53b877SKees Cook 		set_return_addr_unchecked(&&normal, &&redirected);
1372e53b877SKees Cook 		fallthrough;
1382e53b877SKees Cook 	case 1:
1392e53b877SKees Cook normal:
1402e53b877SKees Cook 		/* Always true */
1412e53b877SKees Cook 		if (!force_check) {
1422e53b877SKees Cook 			pr_err("FAIL: stack return address manipulation failed!\n");
1432e53b877SKees Cook 			/* If we can't redirect "normally", we can't test mitigations. */
1442e53b877SKees Cook 			return;
1452e53b877SKees Cook 		}
1462e53b877SKees Cook 		break;
1472e53b877SKees Cook 	default:
1482e53b877SKees Cook redirected:
1492e53b877SKees Cook 		pr_info("ok: redirected stack return address.\n");
1502e53b877SKees Cook 		break;
1512e53b877SKees Cook 	}
1522e53b877SKees Cook 
1532e53b877SKees Cook 	pr_info("Attempting checked stack return address redirection ...\n");
1542e53b877SKees Cook 
1552e53b877SKees Cook 	switch (force_check) {
1562e53b877SKees Cook 	case 0:
1572e53b877SKees Cook 		set_return_addr(&&check_normal, &&check_redirected);
1582e53b877SKees Cook 		fallthrough;
1592e53b877SKees Cook 	case 1:
1602e53b877SKees Cook check_normal:
1612e53b877SKees Cook 		/* Always true */
1622e53b877SKees Cook 		if (!force_check) {
1632e53b877SKees Cook 			pr_info("ok: control flow unchanged.\n");
1642e53b877SKees Cook 			return;
1652e53b877SKees Cook 		}
1662e53b877SKees Cook 
1672e53b877SKees Cook check_redirected:
1682e53b877SKees Cook 		pr_err("FAIL: stack return address was redirected!\n");
1692e53b877SKees Cook 		break;
1702e53b877SKees Cook 	}
1712e53b877SKees Cook 
1722e53b877SKees Cook 	if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) {
1732e53b877SKees Cook 		pr_expected_config(CONFIG_ARM64_PTR_AUTH_KERNEL);
1742e53b877SKees Cook 		return;
1752e53b877SKees Cook 	}
1762e53b877SKees Cook 	if (IS_ENABLED(CONFIG_SHADOW_CALL_STACK)) {
1772e53b877SKees Cook 		pr_expected_config(CONFIG_SHADOW_CALL_STACK);
1782e53b877SKees Cook 		return;
1792e53b877SKees Cook 	}
1802e53b877SKees Cook 	pr_warn("This is probably expected, since this %s was built *without* %s=y nor %s=y\n",
1812e53b877SKees Cook 		lkdtm_kernel_info,
1822e53b877SKees Cook 		"CONFIG_ARM64_PTR_AUTH_KERNEL", "CONFIG_SHADOW_CALL_STACK");
1832e53b877SKees Cook }
1842e53b877SKees Cook 
18573f62e60SKees Cook static struct crashtype crashtypes[] = {
18673f62e60SKees Cook 	CRASHTYPE(CFI_FORWARD_PROTO),
1872e53b877SKees Cook 	CRASHTYPE(CFI_BACKWARD),
18873f62e60SKees Cook };
18973f62e60SKees Cook 
19073f62e60SKees Cook struct crashtype_category cfi_crashtypes = {
19173f62e60SKees Cook 	.crashtypes = crashtypes,
19273f62e60SKees Cook 	.len	    = ARRAY_SIZE(crashtypes),
19373f62e60SKees Cook };
194