1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "crash-handler.h"
6 
7 #include <inttypes.h>
8 
9 #include <zircon/process.h>
10 #include <zircon/status.h>
11 #include <zircon/syscalls/exception.h>
12 #include <zircon/syscalls/port.h>
13 #include <zircon/threads.h>
14 
15 #define EXCEPTION_PORT_KEY 1
16 // The test completed without the test thread crashing.
17 #define TEST_ENDED_EVENT_KEY 2
18 // The test thread had a registered crash.
19 #define TEST_THREAD_TERMINATED_KEY 3
20 
21 // Signals sent from the test thread to the crash handler port to indicate
22 // the test result.
23 #define TEST_PASSED_SIGNAL ZX_USER_SIGNAL_0
24 #define TEST_FAILED_SIGNAL ZX_USER_SIGNAL_1
25 
26 /**
27  * Kills the crashing process or thread found in the registered list matching
28  * the exception report. Processes or threads are registered in tests via
29  * REGISTER_CRASH if a crash is expected.
30  *
31  * If killing failed, the test will be terminated.
32  *
33  * If the crash was not registered, it will be bubbled up to the crashlogger,
34  * and then the test will be terminated.
35  */
process_exception(crash_list_t crash_list,const zx_port_packet_t * packet,zx_handle_t exception_port)36 static void process_exception(crash_list_t crash_list, const zx_port_packet_t* packet,
37                               zx_handle_t exception_port) {
38     const zx_packet_exception_t* exception = &packet->exception;
39 
40     // Check for exceptions from registered processes that are not really crashes.
41     switch (packet->type) {
42     case ZX_EXCP_THREAD_STARTING:
43     case ZX_EXCP_THREAD_EXITING: {
44         zx_handle_t process = crash_list_lookup_koid(crash_list, exception->pid);
45         zx_handle_t thread = ZX_HANDLE_INVALID;
46         if (process == ZX_HANDLE_INVALID) {
47             // The test may have registered a thread handle instead.
48             thread = crash_list_lookup_koid(crash_list, exception->tid);
49         }
50         if (process != ZX_HANDLE_INVALID || thread != ZX_HANDLE_INVALID) {
51             zx_status_t status;
52             if (thread == ZX_HANDLE_INVALID) {
53                 status =
54                     zx_object_get_child(process, exception->tid, ZX_RIGHT_SAME_RIGHTS, &thread);
55                 if (status != ZX_OK) {
56                     UNITTEST_FAIL_TRACEF(
57                         "FATAL: failed to get a handle to [%" PRIu64 "%." PRIu64 "] : error %s\n",
58                         exception->pid, exception->tid, zx_status_get_string(status));
59                     exit(ZX_ERR_INTERNAL);
60                 }
61             }
62             status = zx_task_resume_from_exception(thread, exception_port, 0);
63             if (status != ZX_OK) {
64                 UNITTEST_FAIL_TRACEF("FATAL: failed to resume [%" PRIu64 ".%" PRIu64
65                                      "] : error %s\n",
66                                      exception->pid, exception->tid, zx_status_get_string(status));
67                 exit(ZX_ERR_INTERNAL);
68             }
69             return;
70         }
71         break;
72     }
73     default:
74         break;
75     }
76 
77     // Check if the crashed process is in the registered list and remove
78     // it if so.
79     zx_handle_t match = crash_list_delete_koid(crash_list, exception->pid);
80     if (match == ZX_HANDLE_INVALID) {
81         // The test may have registered a thread handle instead.
82         match = crash_list_delete_koid(crash_list, exception->tid);
83     }
84 
85     // The crash was not registered. We should let crashlogger print out the
86     // details and then fail the test.
87     if (match == ZX_HANDLE_INVALID) {
88         UNITTEST_FAIL_TRACEF("FATAL: [%" PRIu64 ".%" PRIu64
89                              "] crashed with exception 0x%x but was not registered\n",
90                              exception->pid, exception->tid, packet->type);
91         zx_handle_t job = zx_job_default();
92         if (job == ZX_HANDLE_INVALID) {
93             UNITTEST_FAIL_TRACEF("FATAL: Unexpected environment. Tests should have a "
94                                  "default job available\n");
95             exit(ZX_ERR_INTERNAL);
96 
97         }
98         zx_handle_t process;
99         zx_status_t status =
100             zx_object_get_child(job, exception->pid, ZX_RIGHT_SAME_RIGHTS, &process);
101         if (status != ZX_OK) {
102             UNITTEST_FAIL_TRACEF("FATAL: failed to get a handle to [%" PRIu64 "] : error %s\n",
103                                  exception->pid, zx_status_get_string(status));
104             exit(ZX_ERR_INTERNAL);
105         }
106         zx_handle_t thread;
107         status = zx_object_get_child(process, exception->tid, ZX_RIGHT_SAME_RIGHTS, &thread);
108         if (status != ZX_OK) {
109             UNITTEST_FAIL_TRACEF("FATAL: failed to get a handle to [%" PRIu64 ".%" PRIu64
110                                  "] : error %s\n",
111                                  exception->pid, exception->tid, zx_status_get_string(status));
112             zx_handle_close(process);
113             exit(ZX_ERR_INTERNAL);
114         }
115         // Pass the exception up to crashlogger.
116         status = zx_task_resume_from_exception(thread, exception_port, ZX_RESUME_TRY_NEXT);
117         if (status == ZX_OK) {
118             // Give crashlogger a little time to print info about the crashed
119             // thread.
120             zx_nanosleep(zx_deadline_after(ZX_MSEC(100)));
121         } else {
122             UNITTEST_FAIL_TRACEF("FATAL: could not pass exception from [%" PRIu64 ".%" PRIu64
123                                  "] : error %s\n",
124                                  exception->pid, exception->tid, zx_status_get_string(status));
125         }
126         // This may not be reached if the test process itself crashed,
127         // as crashlogger will kill the crashed process.
128         zx_handle_close(process);
129         zx_handle_close(thread);
130         // TODO: fail the test more gracefully.
131         exit(ZX_ERR_INTERNAL);
132     }
133     zx_status_t status = zx_task_kill(match);
134     if (status != ZX_OK) {
135         UNITTEST_FAIL_TRACEF("FATAL: failed to kill [%" PRIu64 ".%" PRIu64 "]  : error %s\n",
136                              exception->pid, exception->tid, zx_status_get_string(status));
137         exit(ZX_ERR_INTERNAL);
138     }
139 
140     // The exception is still unprocessed. We should wait for termination so
141     // there is no race condition with when we unbind the exception port.
142     status = zx_object_wait_one(match, ZX_TASK_TERMINATED, ZX_TIME_INFINITE, NULL);
143     if (status != ZX_OK) {
144         UNITTEST_FAIL_TRACEF("FATAL: failed to wait for termination  : error %s\n",
145                              zx_status_get_string(status));
146         exit(ZX_ERR_INTERNAL);
147     }
148     zx_handle_close(match);
149 }
150 
151 // Returns the test result if it completes, else true if the test thread
152 // had a registered crash.
watch_test_thread(zx_handle_t port,crash_list_t crash_list)153 static test_result_t watch_test_thread(zx_handle_t port, crash_list_t crash_list) {
154     zx_port_packet_t packet;
155     while (true) {
156         zx_status_t status = zx_port_wait(port, ZX_TIME_INFINITE, &packet);
157         if (status != ZX_OK) {
158             UNITTEST_FAIL_TRACEF("failed to wait on port: error %s\n",
159                                  zx_status_get_string(status));
160             exit(ZX_ERR_INTERNAL);
161         }
162         switch (packet.key) {
163         case EXCEPTION_PORT_KEY:
164             process_exception(crash_list, &packet, port);
165             break;
166         case TEST_ENDED_EVENT_KEY:
167             if (packet.signal.observed & TEST_PASSED_SIGNAL) {
168                 return TEST_PASSED;
169             } else if (packet.signal.observed & TEST_FAILED_SIGNAL) {
170                 return TEST_FAILED;
171             } else {
172                 UNITTEST_FAIL_TRACEF("unknown test ended event signal: %u\n",
173                                      packet.signal.observed);
174                 exit(ZX_ERR_INTERNAL);
175             }
176         case TEST_THREAD_TERMINATED_KEY:
177             // The test thread exited without sending the
178             // TEST_ENDED_EVENT_KEY packet, so we must have killed the crashing
179             // thread. If it was an unregistered crash, we would have exited
180             // and failed the test already, so this must be a registered crash.
181             return TEST_CRASHED;
182         }
183     }
184     __UNREACHABLE;
185 }
186 
187 struct test_data_t {
188     // The test function to call.
189     bool (*test_function)(void*);
190     void* test_function_arg;
191 
192     // For signaling TEST_PASSED_SIGNAL or TEST_FAILED_SIGNAL.
193     zx_handle_t test_ended_event;
194     // For registering test termination.
195     zx_handle_t port;
196 
197     // For registering the test thread, if it is expected to crash.
198     crash_list_t crash_list;
199     // Whether to bind to the thread exception port.
200     bool bind_to_thread;
201 };
202 
203 // This is run as a separate thread, so exit() is used instead of returning
204 // status values.
run_test(void * arg)205 static int run_test(void* arg) {
206     test_data_t* data = (test_data_t*)arg;
207     zx_handle_t self = zx_thread_self();
208 
209     // We need to register for thread termination here instead of the main
210     // thread. The main thread can't get a handle to this thread before it has
211     // started, at which point the test may have run and crashed already,
212     // leading to an invalid handle.
213     zx_status_t status = zx_object_wait_async(self, data->port, TEST_THREAD_TERMINATED_KEY,
214                                               ZX_THREAD_TERMINATED, ZX_WAIT_ASYNC_ONCE);
215     if (status != ZX_OK) {
216         UNITTEST_FAIL_TRACEF("FATAL: failed to wait on test thread termination : error %s\n",
217                              zx_status_get_string(status));
218         exit(ZX_ERR_INTERNAL);
219     }
220 
221     // We also can't do this in the main thread as we wouldn't have the
222     // thread handle yet.
223     if (data->bind_to_thread) {
224         status = zx_task_bind_exception_port(self, data->port, EXCEPTION_PORT_KEY, 0);
225         if (status != ZX_OK) {
226             UNITTEST_FAIL_TRACEF("FATAL: failed to bind to exception port: error %s\n",
227                                  zx_status_get_string(status));
228             exit(ZX_ERR_INTERNAL);
229         }
230         crash_list_register(data->crash_list, self);
231     }
232 
233     bool test_result = data->test_function(data->test_function_arg);
234 
235     // Notify the crash handler of the test result before returning.
236     // We can't just return the test result as the test thread could
237     // be registered to crash, so the crash handler can't use thrd_join.
238     uint32_t signal = test_result ? TEST_PASSED_SIGNAL : TEST_FAILED_SIGNAL;
239     status = zx_object_signal(data->test_ended_event, 0, signal);
240     if (status != ZX_OK) {
241         UNITTEST_FAIL_TRACEF("FATAL: failed to signal test result : error %s\n",
242                              zx_status_get_string(status));
243         exit(ZX_ERR_INTERNAL);
244     }
245     return 0;
246 }
247 
248 // Runs the function in a separate thread with the given argument,
249 // catching any crashes.
250 // If bind_to_job is true, this will bind to the job exception port
251 // before starting the test.
252 // If false, this will bind to the test thread's exception port once started
253 // and add the thread to the expected crashes list.
run_with_crash_handler(crash_list_t crash_list,bool (* fn_to_run)(void *),void * arg,bool bind_to_job,test_result_t * test_result)254 static zx_status_t run_with_crash_handler(crash_list_t crash_list,
255                                           bool (*fn_to_run)(void*), void* arg,
256                                           bool bind_to_job,
257                                           test_result_t* test_result) {
258     zx_handle_t port;
259     zx_status_t status = zx_port_create(0, &port);
260     if (status != ZX_OK) {
261         UNITTEST_FAIL_TRACEF("failed to create port: error %s\n", zx_status_get_string(status));
262         return status;
263     }
264     if (bind_to_job) {
265         status = zx_task_bind_exception_port(zx_job_default(), port, EXCEPTION_PORT_KEY, 0);
266         if (status != ZX_OK) {
267             UNITTEST_FAIL_TRACEF("failed to bind to exception port: error %s\n",
268                                  zx_status_get_string(status));
269             zx_handle_close(port);
270             return status;
271         }
272     }
273 
274     zx_handle_t test_ended_event;
275     status = zx_event_create(0, &test_ended_event);
276     if (status != ZX_OK) {
277         UNITTEST_FAIL_TRACEF("failed to create event: error %s\n", zx_status_get_string(status));
278         zx_handle_close(port);
279         return status;
280     }
281     status = zx_object_wait_async(test_ended_event, port, TEST_ENDED_EVENT_KEY,
282                                   TEST_PASSED_SIGNAL | TEST_FAILED_SIGNAL, ZX_WAIT_ASYNC_ONCE);
283     if (status != ZX_OK) {
284         UNITTEST_FAIL_TRACEF("failed to wait on test_ended_event: error %s\n",
285                              zx_status_get_string(status));
286         zx_handle_close(port);
287         zx_handle_close(test_ended_event);
288         return status;
289     }
290 
291     // Run the test in a separate thread in case it crashes.
292     thrd_t test_thread;
293     test_data_t test_data = {.test_function = fn_to_run,
294                              .test_function_arg = arg,
295                              .test_ended_event = test_ended_event,
296                              .port = port,
297                              .crash_list = crash_list,
298                              .bind_to_thread = !bind_to_job};
299 
300     int thrd_res = thrd_create(&test_thread, run_test, (void*)&test_data);
301     if (thrd_res != thrd_success) {
302         UNITTEST_FAIL_TRACEF("failed to create test thread\n");
303         zx_handle_close(port);
304         zx_handle_close(test_ended_event);
305         return thrd_status_to_zx_status(thrd_res);
306     }
307 
308     // The test thread will signal on the test_ended event when it completes,
309     // or the crash handler will catch it crashing.
310     *test_result = watch_test_thread(port, crash_list);
311 
312     zx_handle_close(port);
313     zx_handle_close(test_ended_event);
314 
315     return ZX_OK;
316 }
317 
318 struct test_wrapper_arg_t {
319     bool (*fn)(void);
320 };
321 
test_wrapper(void * arg)322 static bool test_wrapper(void* arg) {
323     return static_cast<test_wrapper_arg_t*>(arg)->fn();
324 }
325 
run_test_with_crash_handler(crash_list_t crash_list,bool (* test_to_run)(),test_result_t * test_result)326 zx_status_t run_test_with_crash_handler(crash_list_t crash_list, bool (*test_to_run)(),
327                                         test_result_t* test_result) {
328     test_wrapper_arg_t twarg = {.fn = test_to_run};
329 
330     return run_with_crash_handler(crash_list, test_wrapper, &twarg, true, test_result);
331 }
332 
333 struct crash_fn_wrapper_arg_t {
334     void (*fn)(void*);
335     void* arg;
336 };
337 
crash_fn_wrapper(void * arg)338 static bool crash_fn_wrapper(void* arg) {
339     crash_fn_wrapper_arg_t* cfwarg = static_cast<crash_fn_wrapper_arg_t*>(arg);
340     cfwarg->fn(cfwarg->arg);
341     // The function is expected to crash and shouldn't get to here.
342     return false;
343 }
344 
run_fn_with_crash_handler(void (* fn_to_run)(void *),void * arg,test_result_t * test_result)345 zx_status_t run_fn_with_crash_handler(void (*fn_to_run)(void*), void* arg,
346                                       test_result_t* test_result) {
347     crash_list_t crash_list = crash_list_new();
348     crash_fn_wrapper_arg_t cfwarg = {.fn = fn_to_run, .arg = arg};
349 
350     zx_status_t status =
351         run_with_crash_handler(crash_list, crash_fn_wrapper, &cfwarg, false, test_result);
352 
353     crash_list_delete(crash_list);
354 
355     return status;
356 }
357