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