brintos

brintos / linux-shallow public Read only

0
0
Text · 5.2 KiB · 6a33889 Raw
203 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * This is for all the tests relating directly to Control Flow Integrity.4 */5#include "lkdtm.h"6#include <asm/page.h>7 8static int called_count;9 10/* Function taking one argument, without a return value. */11static noinline void lkdtm_increment_void(int *counter)12{13	(*counter)++;14}15 16/* Function taking one argument, returning int. */17static noinline int lkdtm_increment_int(int *counter)18{19	(*counter)++;20 21	return *counter;22}23 24/* Don't allow the compiler to inline the calls. */25static noinline void lkdtm_indirect_call(void (*func)(int *))26{27	func(&called_count);28}29 30/*31 * This tries to call an indirect function with a mismatched prototype.32 */33static void lkdtm_CFI_FORWARD_PROTO(void)34{35	/*36	 * Matches lkdtm_increment_void()'s prototype, but not37	 * lkdtm_increment_int()'s prototype.38	 */39	pr_info("Calling matched prototype ...\n");40	lkdtm_indirect_call(lkdtm_increment_void);41 42	pr_info("Calling mismatched prototype ...\n");43	lkdtm_indirect_call((void *)lkdtm_increment_int);44 45	pr_err("FAIL: survived mismatched prototype function call!\n");46	pr_expected_config(CONFIG_CFI_CLANG);47}48 49/*50 * This can stay local to LKDTM, as there should not be a production reason51 * to disable PAC && SCS.52 */53#ifdef CONFIG_ARM64_PTR_AUTH_KERNEL54# ifdef CONFIG_ARM64_BTI_KERNEL55#  define __no_pac             "branch-protection=bti"56# else57#  ifdef CONFIG_CC_HAS_BRANCH_PROT_PAC_RET58#   define __no_pac            "branch-protection=none"59#  else60#   define __no_pac            "sign-return-address=none"61#  endif62# endif63# define __no_ret_protection   __noscs __attribute__((__target__(__no_pac)))64#else65# define __no_ret_protection   __noscs66#endif67 68#define no_pac_addr(addr)      \69	((__force __typeof__(addr))((uintptr_t)(addr) | PAGE_OFFSET))70 71#ifdef CONFIG_RISCV72/* https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#frame-pointer-convention */73#define FRAME_RA_OFFSET		(-1)74#else75#define FRAME_RA_OFFSET		176#endif77 78/* The ultimate ROP gadget. */79static noinline __no_ret_protection80void set_return_addr_unchecked(unsigned long *expected, unsigned long *addr)81{82	/* Use of volatile is to make sure final write isn't seen as a dead store. */83	unsigned long * volatile *ret_addr =84		(unsigned long **)__builtin_frame_address(0) + FRAME_RA_OFFSET;85 86	/* Make sure we've found the right place on the stack before writing it. */87	if (no_pac_addr(*ret_addr) == expected)88		*ret_addr = (addr);89	else90		/* Check architecture, stack layout, or compiler behavior... */91		pr_warn("Eek: return address mismatch! %px != %px\n",92			*ret_addr, addr);93}94 95static noinline96void set_return_addr(unsigned long *expected, unsigned long *addr)97{98	/* Use of volatile is to make sure final write isn't seen as a dead store. */99	unsigned long * volatile *ret_addr =100		(unsigned long **)__builtin_frame_address(0) + FRAME_RA_OFFSET;101 102	/* Make sure we've found the right place on the stack before writing it. */103	if (no_pac_addr(*ret_addr) == expected)104		*ret_addr = (addr);105	else106		/* Check architecture, stack layout, or compiler behavior... */107		pr_warn("Eek: return address mismatch! %px != %px\n",108			*ret_addr, addr);109}110 111static volatile int force_check;112 113static void lkdtm_CFI_BACKWARD(void)114{115	/* Use calculated gotos to keep labels addressable. */116	void *labels[] = { NULL, &&normal, &&redirected, &&check_normal, &&check_redirected };117 118	pr_info("Attempting unchecked stack return address redirection ...\n");119 120	/* Always false */121	if (force_check) {122		/*123		 * Prepare to call with NULLs to avoid parameters being treated as124		 * constants in -02.125		 */126		set_return_addr_unchecked(NULL, NULL);127		set_return_addr(NULL, NULL);128		if (force_check)129			goto *labels[1];130		if (force_check)131			goto *labels[2];132		if (force_check)133			goto *labels[3];134		if (force_check)135			goto *labels[4];136		return;137	}138 139	/*140	 * Use fallthrough switch case to keep basic block ordering between141	 * set_return_addr*() and the label after it.142	 */143	switch (force_check) {144	case 0:145		set_return_addr_unchecked(&&normal, &&redirected);146		fallthrough;147	case 1:148normal:149		/* Always true */150		if (!force_check) {151			pr_err("FAIL: stack return address manipulation failed!\n");152			/* If we can't redirect "normally", we can't test mitigations. */153			return;154		}155		break;156	default:157redirected:158		pr_info("ok: redirected stack return address.\n");159		break;160	}161 162	pr_info("Attempting checked stack return address redirection ...\n");163 164	switch (force_check) {165	case 0:166		set_return_addr(&&check_normal, &&check_redirected);167		fallthrough;168	case 1:169check_normal:170		/* Always true */171		if (!force_check) {172			pr_info("ok: control flow unchanged.\n");173			return;174		}175 176check_redirected:177		pr_err("FAIL: stack return address was redirected!\n");178		break;179	}180 181	if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) {182		pr_expected_config(CONFIG_ARM64_PTR_AUTH_KERNEL);183		return;184	}185	if (IS_ENABLED(CONFIG_SHADOW_CALL_STACK)) {186		pr_expected_config(CONFIG_SHADOW_CALL_STACK);187		return;188	}189	pr_warn("This is probably expected, since this %s was built *without* %s=y nor %s=y\n",190		lkdtm_kernel_info,191		"CONFIG_ARM64_PTR_AUTH_KERNEL", "CONFIG_SHADOW_CALL_STACK");192}193 194static struct crashtype crashtypes[] = {195	CRASHTYPE(CFI_FORWARD_PROTO),196	CRASHTYPE(CFI_BACKWARD),197};198 199struct crashtype_category cfi_crashtypes = {200	.crashtypes = crashtypes,201	.len	    = ARRAY_SIZE(crashtypes),202};203