cfi.c (4909B)
1// SPDX-License-Identifier: GPL-2.0 2/* 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 * This tries to call an indirect function with a mismatched prototype. 25 */ 26static void lkdtm_CFI_FORWARD_PROTO(void) 27{ 28 /* 29 * Matches lkdtm_increment_void()'s prototype, but not 30 * lkdtm_increment_int()'s prototype. 31 */ 32 void (*func)(int *); 33 34 pr_info("Calling matched prototype ...\n"); 35 func = lkdtm_increment_void; 36 func(&called_count); 37 38 pr_info("Calling mismatched prototype ...\n"); 39 func = (void *)lkdtm_increment_int; 40 func(&called_count); 41 42 pr_err("FAIL: survived mismatched prototype function call!\n"); 43 pr_expected_config(CONFIG_CFI_CLANG); 44} 45 46/* 47 * This can stay local to LKDTM, as there should not be a production reason 48 * to disable PAC && SCS. 49 */ 50#ifdef CONFIG_ARM64_PTR_AUTH_KERNEL 51# ifdef CONFIG_ARM64_BTI_KERNEL 52# define __no_pac "branch-protection=bti" 53# else 54# define __no_pac "branch-protection=none" 55# endif 56# define __no_ret_protection __noscs __attribute__((__target__(__no_pac))) 57#else 58# define __no_ret_protection __noscs 59#endif 60 61#define no_pac_addr(addr) \ 62 ((__force __typeof__(addr))((uintptr_t)(addr) | PAGE_OFFSET)) 63 64/* The ultimate ROP gadget. */ 65static noinline __no_ret_protection 66void set_return_addr_unchecked(unsigned long *expected, unsigned long *addr) 67{ 68 /* Use of volatile is to make sure final write isn't seen as a dead store. */ 69 unsigned long * volatile *ret_addr = (unsigned long **)__builtin_frame_address(0) + 1; 70 71 /* Make sure we've found the right place on the stack before writing it. */ 72 if (no_pac_addr(*ret_addr) == expected) 73 *ret_addr = (addr); 74 else 75 /* Check architecture, stack layout, or compiler behavior... */ 76 pr_warn("Eek: return address mismatch! %px != %px\n", 77 *ret_addr, addr); 78} 79 80static noinline 81void set_return_addr(unsigned long *expected, unsigned long *addr) 82{ 83 /* Use of volatile is to make sure final write isn't seen as a dead store. */ 84 unsigned long * volatile *ret_addr = (unsigned long **)__builtin_frame_address(0) + 1; 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 else 90 /* Check architecture, stack layout, or compiler behavior... */ 91 pr_warn("Eek: return address mismatch! %px != %px\n", 92 *ret_addr, addr); 93} 94 95static volatile int force_check; 96 97static void lkdtm_CFI_BACKWARD(void) 98{ 99 /* Use calculated gotos to keep labels addressable. */ 100 void *labels[] = {0, &&normal, &&redirected, &&check_normal, &&check_redirected}; 101 102 pr_info("Attempting unchecked stack return address redirection ...\n"); 103 104 /* Always false */ 105 if (force_check) { 106 /* 107 * Prepare to call with NULLs to avoid parameters being treated as 108 * constants in -02. 109 */ 110 set_return_addr_unchecked(NULL, NULL); 111 set_return_addr(NULL, NULL); 112 if (force_check) 113 goto *labels[1]; 114 if (force_check) 115 goto *labels[2]; 116 if (force_check) 117 goto *labels[3]; 118 if (force_check) 119 goto *labels[4]; 120 return; 121 } 122 123 /* 124 * Use fallthrough switch case to keep basic block ordering between 125 * set_return_addr*() and the label after it. 126 */ 127 switch (force_check) { 128 case 0: 129 set_return_addr_unchecked(&&normal, &&redirected); 130 fallthrough; 131 case 1: 132normal: 133 /* Always true */ 134 if (!force_check) { 135 pr_err("FAIL: stack return address manipulation failed!\n"); 136 /* If we can't redirect "normally", we can't test mitigations. */ 137 return; 138 } 139 break; 140 default: 141redirected: 142 pr_info("ok: redirected stack return address.\n"); 143 break; 144 } 145 146 pr_info("Attempting checked stack return address redirection ...\n"); 147 148 switch (force_check) { 149 case 0: 150 set_return_addr(&&check_normal, &&check_redirected); 151 fallthrough; 152 case 1: 153check_normal: 154 /* Always true */ 155 if (!force_check) { 156 pr_info("ok: control flow unchanged.\n"); 157 return; 158 } 159 160check_redirected: 161 pr_err("FAIL: stack return address was redirected!\n"); 162 break; 163 } 164 165 if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL)) { 166 pr_expected_config(CONFIG_ARM64_PTR_AUTH_KERNEL); 167 return; 168 } 169 if (IS_ENABLED(CONFIG_SHADOW_CALL_STACK)) { 170 pr_expected_config(CONFIG_SHADOW_CALL_STACK); 171 return; 172 } 173 pr_warn("This is probably expected, since this %s was built *without* %s=y nor %s=y\n", 174 lkdtm_kernel_info, 175 "CONFIG_ARM64_PTR_AUTH_KERNEL", "CONFIG_SHADOW_CALL_STACK"); 176} 177 178static struct crashtype crashtypes[] = { 179 CRASHTYPE(CFI_FORWARD_PROTO), 180 CRASHTYPE(CFI_BACKWARD), 181}; 182 183struct crashtype_category cfi_crashtypes = { 184 .crashtypes = crashtypes, 185 .len = ARRAY_SIZE(crashtypes), 186};