1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define TLOG_TAG "cfi-crasher"
18 
19 #include <assert.h>
20 #include <lib/tipc/tipc.h>
21 #include <lib/tipc/tipc_srv.h>
22 #include <lk/err_ptr.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <trusty/time.h>
26 #include <trusty_log.h>
27 #include <uapi/err.h>
28 
29 #include <cfi-crasher.h>
30 #include <cfi_crasher_consts.h>
31 
32 static int crasher_on_message(const struct tipc_port* port,
33                               handle_t chan,
34                               void* ctx);
35 
36 static struct tipc_port_acl crasher_port_acl = {
37         .flags = IPC_PORT_ALLOW_TA_CONNECT,
38         .uuid_num = 0,
39         .uuids = NULL,
40         .extra_data = NULL,
41 };
42 
43 static struct tipc_port crasher_port = {
44         .name = CFI_CRASHER_PORT,
45         .msg_max_size = sizeof(struct crasher_msg),
46         .msg_queue_len = 1,
47         .acl = &crasher_port_acl,
48         .priv = NULL,
49 };
50 
51 typedef int (*int_func)(int);
52 
53 /*
54  * function against which all subsequent functions are compared.
55  * cfi-icall ensures that control is only transferred to a function with
56  * the same static type.
57  */
crasher_int_func(int arg)58 __NO_INLINE static int crasher_int_func(int arg) {
59     TLOG("function with int return type and 1 int arg ran\n");
60     return arg;
61 }
62 
crasher_float_func(float arg)63 __NO_INLINE static float crasher_float_func(float arg) {
64     TLOG("function with float return type and 1 float arg ran\n");
65     return arg;
66 }
67 
68 /*
69  * We need a global variable for the channel handle since crasher_void
70  * doesn't take arguments. Additionally, we need a global variable for
71  * the program counter in order to calculate the address for jumping
72  * partway into crasher_not_entry_func.
73  */
74 handle_t global_chan;
75 void* volatile global_pc;
crasher_void(void)76 __NO_INLINE static void crasher_void(void) {
77     TLOG("crasher_not_entry_func ran, logging from crasher_void\n");
78     /*
79      * We suppress sending a tipc message in the case where global_pc is
80      * set since in this case crasher_not_entry_func was properly called
81      * so we expect to send CRASHER_ENTRY back to the client and want to
82      * suppress a second CRASHER_VOID message. Also, crasher_not_entry_func
83      * is called properly by CRASHER_NOT_ENTRY in order to first calculate
84      * the offset for jumping partway into the function, so we do not want
85      * to send a tipc message in this case. This if statement is placed
86      * in crasher_void instead of crasher_not_entry_func to minimize
87      * conditionals in a function entered partway, as this could lead
88      * to crashing even without CFI enabled.
89      */
90     if (global_pc) {
91         return;
92     }
93 
94     struct crasher_msg msg;
95     msg.cmd = CRASHER_VOID;
96     /*
97      * Because entering crasher_not_entry_func partway skips the
98      * preamble, regardless of whether CFI is enabled the app will
99      * crash. We need to know whether the app crashes immediately
100      * after jumping to crasher_not_entry_func due to CFI, or if
101      * it crashed because the stack is corrupted. So we send a
102      * message back to communicate that the app is not crashing
103      * due to CFI if crasher_void runs.
104      */
105     int ret = tipc_send1(global_chan, &msg, sizeof(msg));
106     if (ret < 0 || ret != sizeof(msg)) {
107         TLOGE("Failed to send message (%d)\n", ret);
108     }
109 }
110 
111 /*
112  * Returns the program counter for the caller's context. We call
113  * get_pc from the beginning of crasher_not_entry_func in order to
114  * obtain a natural entry point partway through crasher_not_entry_func.
115  */
get_pc(void)116 __NO_INLINE static void* get_pc(void) {
117     return __builtin_extract_return_addr(__builtin_return_address(0));
118 }
119 
crasher_not_entry_func(int arg)120 __NO_INLINE static int crasher_not_entry_func(int arg) {
121     global_pc = get_pc();
122     /*
123      * We place logging in a separate function call since otherwise
124      * arguments of TLOG are corrupted by jumping into the function partway.
125      */
126     crasher_void();
127 
128     /*
129      * A segfault occurs if the indirect call skips the function preamble.
130      * To ensure the test app does not interpret this segfault as CFI
131      * crashing the app, we send a message from crasher_void. This way, the
132      * test app sees the message before seeing the channel has closed,
133      * preventing the test from incorrectly passing. With CFI enabled,
134      * crasher_void should never run if a test attempts to enter
135      * crasher_not_entry_func partway. We sleep for 100 ms so that test app sees
136      * the response message and channel closed event as separate events.
137      */
138     trusty_nanosleep(0, 0, 100 * 1000 * 1000);
139 
140     /* Log point to prevent tail-call optimisation to trusty_nanosleep(),
141      * which can cause a crash before the sleep with certain CFI techniques
142      * such as PAC.
143      */
144     TLOGI("crasher_not_entry_func done\n");
145 
146     return arg;
147 }
148 
149 /*
150  * We call this function instead of directly calling crasher_float_func
151  * in crasher_on_message since no_sanitize("cfi-icall") must be specified
152  * for the function which calls crasher_float_func in order to exempt
153  * it from CFI.
154  */
155 __NO_INLINE static void __attribute__((no_sanitize("cfi-icall")))
crasher_call_exclude_float_func(void)156 crasher_call_exclude_float_func(void) {
157     TLOG("crasher_call_exclude_float_func ran, calling crasher_float_func\n");
158     ((int_func)crasher_float_func)(0);
159 }
160 
161 /*
162  * Similar to crasher_call_exclude_float_func,
163  * crasher_call_exclude_not_entry_func is a wrapper function which ensures
164  * a CFI-exempt context when attempting to enter crasher_not_entry_func
165  * partway.
166  */
167 __NO_INLINE static void __attribute__((no_sanitize("cfi-icall")))
crasher_call_exclude_not_entry_func(void)168 crasher_call_exclude_not_entry_func(void) {
169     TLOG("crasher_call_exclude_not_entry_func ran, entering crasher_not_entry_func partway\n");
170     /*
171      * Because global_pc is global, we reset it before each test to make sure
172      * different tests do not interfere with each other.
173      */
174     global_pc = NULL;
175     crasher_not_entry_func(0); /* set global_pc by calling correctly */
176     /*
177      * We need to reset global_pc so the crasher_void tipc message is not
178      * suppressed. We save global_pc into crasher_not_entry_func_partway before
179      * resetting  it.
180      */
181     void* crasher_not_entry_func_partway = global_pc;
182     global_pc = NULL;
183     ((int_func)(crasher_not_entry_func_partway))(0);
184 }
185 
crasher_on_message(const struct tipc_port * port,handle_t chan,void * ctx)186 static int crasher_on_message(const struct tipc_port* port,
187                               handle_t chan,
188                               void* ctx) {
189     struct crasher_msg msg;
190 
191     int ret = tipc_recv1(chan, sizeof(msg), &msg, sizeof(msg));
192     if (ret < 0 || ret != sizeof(msg)) {
193         TLOGE("Failed to receive message (%d)\n", ret);
194         return ret;
195     }
196 
197     switch (msg.cmd) {
198     /*
199      * CRASHER_NOP test checks that the internal testing machinery
200      * is working properly even when crasher_on_message does not transfer
201      * control flow to another internal function. Since the last 2 tests
202      * are expected to crash the app when CFI is enabled, we need to make
203      * sure the app isn't just always crashing.
204      */
205     case CRASHER_NOP:
206         TLOGI("nop\n");
207         break;
208     /*
209      * Similar to CRASHER_NOP, CRASHER_CORRECT is expected to not crash
210      * the app regardless of whether CFI is enabled since we are calling
211      * a function with the expected signature. We want to ensure the app
212      * is not just always crashing.
213      */
214     case CRASHER_CORRECT:
215         TLOGI("call crasher_int_func\n");
216         /*
217          * The argument is arbitrary, we just follow a convention of using
218          * 0, for simplicity.
219          */
220         crasher_int_func(0);
221         break;
222     /*
223      * Similar to how CRASHER_CORRECT ensures the app doesn't crash when
224      * calling crasher_int_func correctly, CRASHER_ENTRY ensures the app
225      * doesn't crash when calling crasher_not_entry_func correctly.
226      */
227     case CRASHER_ENTRY:
228         TLOGI("call crasher_not_entry_func\n");
229         global_chan = chan;
230         global_pc = NULL;
231         crasher_not_entry_func(0);
232         break;
233     /*
234      * Part of testing that CFI is working properly includes testing that
235      * exempting certain functions from CFI works as intended.
236      * CRASHER_EXCLUDE_WRONG_TYPE ensures that calling crasher_float_func
237      * from a CFI-exempt context does not crash the app. This test essentially
238      * checks that the no_sanitize("cfi-icall") function decorator on
239      * crasher_call_exclude_float_func works as intended.
240      */
241     case CRASHER_EXCLUDE_WRONG_TYPE:
242         TLOGI("call crasher_call_exclude_float_func\n");
243         crasher_call_exclude_float_func();
244         break;
245     /*
246      * Similar to CRASHER_EXCLUDE_WRONG_TYPE, CRASHER_EXCLUDE_NOT_ENTRY
247      * tests that entering crasher_not_entry_func partway from a CFI-exempt
248      * context does not cause a CFI-induced crash.
249      */
250     case CRASHER_EXCLUDE_NOT_ENTRY:
251         TLOGI("call crasher_call_exclude_not_entry_func\n");
252         global_chan = chan;
253         crasher_call_exclude_not_entry_func();
254         break;
255     /*
256      * CRASHER_WRONG_TYPE is the first test expected to crash when CFI
257      * is enabled. CFI ensures that control is only transferred to functions
258      * with the expected signature. Since we cast crasher_float_func to an
259      * int_func function pointer when it is actually a float_func, we expect
260      * CFI to crash the app when transferring control to crasher_float_func.
261      * If the app does not crash, then cfi-icall is not enabled.
262      */
263     case CRASHER_WRONG_TYPE:
264         TLOGI("call crasher_float_func\n");
265         ((int_func)crasher_float_func)(0);
266         break;
267     /*
268      * In addition to checking the function signature, cfi-icall ensures that
269      * control is only transferred to valid function entry points.
270      * When CFI is enabled, control should not be transferred to the middle of
271      * crasher_not_entry_func.
272      */
273     case CRASHER_NOT_ENTRY:
274         TLOGI("enter crasher_not_entry_func partway\n");
275         global_chan = chan;
276         global_pc = NULL;
277         crasher_not_entry_func(0);
278         void* crasher_not_entry_func_partway = global_pc;
279         global_pc = NULL;
280         ((int_func)(crasher_not_entry_func_partway))(0);
281         break;
282     default:
283         TLOGE("Bad command: %d\n", msg.cmd);
284         msg.cmd = CRASHER_BAD_CMD;
285     }
286     /*
287      * We echo the incoming command in the case where the crasher app
288      * runs the test without crashing. This is effectively saying "did
289      * not crash when executing command X."
290      */
291     ret = tipc_send1(chan, &msg, sizeof(msg));
292     if (ret < 0 || ret != sizeof(msg)) {
293         TLOGE("Failed to send message (%d)\n", ret);
294         return ret < 0 ? ret : ERR_IO;
295     }
296 
297     return 0;
298 }
299 
300 static struct tipc_srv_ops crasher_ops = {
301         .on_message = crasher_on_message,
302 };
303 
main(void)304 int main(void) {
305     struct tipc_hset* hset = tipc_hset_create();
306     if (IS_ERR(hset)) {
307         TLOGE("Failed to create handle set (%d)\n", PTR_ERR(hset));
308         return PTR_ERR(hset);
309     }
310 
311     int rc = tipc_add_service(hset, &crasher_port, 1, 1, &crasher_ops);
312     if (rc < 0) {
313         TLOGE("Failed to add service (%d)\n", rc);
314         return rc;
315     }
316 
317     /* if app exits, kernel will log that */
318     return tipc_run_event_loop(hset);
319 }
320