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 | |
8 | static int called_count; |
9 | |
10 | /* Function taking one argument, without a return value. */ |
11 | static noinline void lkdtm_increment_void(int *counter) |
12 | { |
13 | (*counter)++; |
14 | } |
15 | |
16 | /* Function taking one argument, returning int. */ |
17 | static 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. */ |
25 | static 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 | */ |
33 | static void lkdtm_CFI_FORWARD_PROTO(void) |
34 | { |
35 | /* |
36 | * Matches lkdtm_increment_void()'s prototype, but not |
37 | * lkdtm_increment_int()'s prototype. |
38 | */ |
39 | pr_info("Calling matched prototype ...\n" ); |
40 | lkdtm_indirect_call(func: lkdtm_increment_void); |
41 | |
42 | pr_info("Calling mismatched prototype ...\n" ); |
43 | lkdtm_indirect_call(func: (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 reason |
51 | * to disable PAC && SCS. |
52 | */ |
53 | #ifdef CONFIG_ARM64_PTR_AUTH_KERNEL |
54 | # ifdef CONFIG_ARM64_BTI_KERNEL |
55 | # define __no_pac "branch-protection=bti" |
56 | # else |
57 | # ifdef CONFIG_CC_HAS_BRANCH_PROT_PAC_RET |
58 | # define __no_pac "branch-protection=none" |
59 | # else |
60 | # define __no_pac "sign-return-address=none" |
61 | # endif |
62 | # endif |
63 | # define __no_ret_protection __noscs __attribute__((__target__(__no_pac))) |
64 | #else |
65 | # define __no_ret_protection __noscs |
66 | #endif |
67 | |
68 | #define no_pac_addr(addr) \ |
69 | ((__force __typeof__(addr))((uintptr_t)(addr) | PAGE_OFFSET)) |
70 | |
71 | #ifdef CONFIG_RISCV |
72 | /* 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 | #else |
75 | #define FRAME_RA_OFFSET 1 |
76 | #endif |
77 | |
78 | /* The ultimate ROP gadget. */ |
79 | static noinline __no_ret_protection |
80 | void 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 | 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 | |
95 | static noinline |
96 | void 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 | else |
106 | /* Check architecture, stack layout, or compiler behavior... */ |
107 | pr_warn("Eek: return address mismatch! %px != %px\n" , |
108 | *ret_addr, addr); |
109 | } |
110 | |
111 | static volatile int force_check; |
112 | |
113 | static 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 as |
124 | * 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 between |
141 | * set_return_addr*() and the label after it. |
142 | */ |
143 | switch (force_check) { |
144 | case 0: |
145 | set_return_addr_unchecked(expected: &&normal, addr: &&redirected); |
146 | fallthrough; |
147 | case 1: |
148 | normal: |
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: |
157 | redirected: |
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(expected: &&check_normal, addr: &&check_redirected); |
167 | fallthrough; |
168 | case 1: |
169 | check_normal: |
170 | /* Always true */ |
171 | if (!force_check) { |
172 | pr_info("ok: control flow unchanged.\n" ); |
173 | return; |
174 | } |
175 | |
176 | check_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 | |
194 | static struct crashtype crashtypes[] = { |
195 | CRASHTYPE(CFI_FORWARD_PROTO), |
196 | CRASHTYPE(CFI_BACKWARD), |
197 | }; |
198 | |
199 | struct crashtype_category cfi_crashtypes = { |
200 | .crashtypes = crashtypes, |
201 | .len = ARRAY_SIZE(crashtypes), |
202 | }; |
203 | |