1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in 12 * the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include "debuggerd/handler.h" 30 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <inttypes.h> 34 #include <linux/futex.h> 35 #include <pthread.h> 36 #include <sched.h> 37 #include <signal.h> 38 #include <stddef.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <sys/capability.h> 43 #include <sys/mman.h> 44 #include <sys/prctl.h> 45 #include <sys/socket.h> 46 #include <sys/syscall.h> 47 #include <sys/uio.h> 48 #include <sys/un.h> 49 #include <sys/wait.h> 50 #include <unistd.h> 51 52 #include <android-base/unique_fd.h> 53 #include <async_safe/log.h> 54 #include <cutils/properties.h> 55 56 #include <libdebuggerd/utility.h> 57 58 #include "dump_type.h" 59 #include "protocol.h" 60 61 using android::base::Pipe; 62 using android::base::unique_fd; 63 64 // see man(2) prctl, specifically the section about PR_GET_NAME 65 #define MAX_TASK_NAME_LEN (16) 66 67 #if defined(__LP64__) 68 #define CRASH_DUMP_NAME "crash_dump64" 69 #else 70 #define CRASH_DUMP_NAME "crash_dump32" 71 #endif 72 73 #define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME 74 75 // Wrappers that directly invoke the respective syscalls, in case the cached values are invalid. 76 #pragma GCC poison getpid gettid 77 static pid_t __getpid() { 78 return syscall(__NR_getpid); 79 } 80 81 static pid_t __gettid() { 82 return syscall(__NR_gettid); 83 } 84 85 static inline void futex_wait(volatile void* ftx, int value) { 86 syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0); 87 } 88 89 class ErrnoRestorer { 90 public: 91 ErrnoRestorer() : saved_errno_(errno) { 92 } 93 94 ~ErrnoRestorer() { 95 errno = saved_errno_; 96 } 97 98 private: 99 int saved_errno_; 100 }; 101 102 extern "C" void debuggerd_fallback_handler(siginfo_t*, ucontext_t*, void*); 103 104 static debuggerd_callbacks_t g_callbacks; 105 106 // Mutex to ensure only one crashing thread dumps itself. 107 static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER; 108 109 // Don't use async_safe_fatal because it exits via abort, which might put us back into 110 // a signal handler. 111 static void __noreturn __printflike(1, 2) fatal(const char* fmt, ...) { 112 va_list args; 113 va_start(args, fmt); 114 async_safe_format_log_va_list(ANDROID_LOG_FATAL, "libc", fmt, args); 115 _exit(1); 116 } 117 118 static void __noreturn __printflike(1, 2) fatal_errno(const char* fmt, ...) { 119 int err = errno; 120 va_list args; 121 va_start(args, fmt); 122 123 char buf[256]; 124 async_safe_format_buffer_va_list(buf, sizeof(buf), fmt, args); 125 fatal("%s: %s", buf, strerror(err)); 126 } 127 128 static bool get_main_thread_name(char* buf, size_t len) { 129 unique_fd fd(open("/proc/self/comm", O_RDONLY | O_CLOEXEC)); 130 if (fd == -1) { 131 return false; 132 } 133 134 ssize_t rc = read(fd, buf, len); 135 if (rc == -1) { 136 return false; 137 } else if (rc == 0) { 138 // Should never happen? 139 return false; 140 } 141 142 // There's a trailing newline, replace it with a NUL. 143 buf[rc - 1] = '\0'; 144 return true; 145 } 146 147 /* 148 * Writes a summary of the signal to the log file. We do this so that, if 149 * for some reason we're not able to contact debuggerd, there is still some 150 * indication of the failure in the log. 151 * 152 * We could be here as a result of native heap corruption, or while a 153 * mutex is being held, so we don't want to use any libc functions that 154 * could allocate memory or hold a lock. 155 */ 156 static void log_signal_summary(const siginfo_t* info) { 157 char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination 158 if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) { 159 strcpy(thread_name, "<name unknown>"); 160 } else { 161 // short names are null terminated by prctl, but the man page 162 // implies that 16 byte names are not. 163 thread_name[MAX_TASK_NAME_LEN] = 0; 164 } 165 166 if (info->si_signo == DEBUGGER_SIGNAL) { 167 async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(), 168 thread_name); 169 return; 170 } 171 172 const char* signal_name = get_signame(info->si_signo); 173 bool has_address = signal_has_si_addr(info->si_signo, info->si_code); 174 175 // Many signals don't have an address. 176 char addr_desc[32] = ""; // ", fault addr 0x1234" 177 if (has_address) { 178 async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr); 179 } 180 181 char main_thread_name[MAX_TASK_NAME_LEN + 1]; 182 if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) { 183 strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name)); 184 } 185 186 async_safe_format_log( 187 ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s), code %d (%s)%s in tid %d (%s), pid %d (%s)", 188 info->si_signo, signal_name, info->si_code, get_sigcode(info->si_signo, info->si_code), 189 addr_desc, __gettid(), thread_name, __getpid(), main_thread_name); 190 } 191 192 /* 193 * Returns true if the handler for signal "signum" has SA_SIGINFO set. 194 */ 195 static bool have_siginfo(int signum) { 196 struct sigaction old_action; 197 if (sigaction(signum, nullptr, &old_action) < 0) { 198 async_safe_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s", 199 strerror(errno)); 200 return false; 201 } 202 return (old_action.sa_flags & SA_SIGINFO) != 0; 203 } 204 205 static void raise_caps() { 206 // Raise CapInh to match CapPrm, so that we can set the ambient bits. 207 __user_cap_header_struct capheader; 208 memset(&capheader, 0, sizeof(capheader)); 209 capheader.version = _LINUX_CAPABILITY_VERSION_3; 210 capheader.pid = 0; 211 212 __user_cap_data_struct capdata[2]; 213 if (capget(&capheader, &capdata[0]) == -1) { 214 fatal_errno("capget failed"); 215 } 216 217 if (capdata[0].permitted != capdata[0].inheritable || 218 capdata[1].permitted != capdata[1].inheritable) { 219 capdata[0].inheritable = capdata[0].permitted; 220 capdata[1].inheritable = capdata[1].permitted; 221 222 if (capset(&capheader, &capdata[0]) == -1) { 223 async_safe_format_log(ANDROID_LOG_ERROR, "libc", "capset failed: %s", strerror(errno)); 224 } 225 } 226 227 // Set the ambient capability bits so that crash_dump gets all of our caps and can ptrace us. 228 uint64_t capmask = capdata[0].inheritable; 229 capmask |= static_cast<uint64_t>(capdata[1].inheritable) << 32; 230 for (unsigned long i = 0; i < 64; ++i) { 231 if (capmask & (1ULL << i)) { 232 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) != 0) { 233 async_safe_format_log(ANDROID_LOG_ERROR, "libc", 234 "failed to raise ambient capability %lu: %s", i, strerror(errno)); 235 } 236 } 237 } 238 } 239 240 static pid_t __fork() { 241 return clone(nullptr, nullptr, 0, nullptr); 242 } 243 244 // Double-clone, with CLONE_FILES to share the file descriptor table for kcmp validation. 245 // Returns 0 in the orphaned child, the pid of the orphan in the original process, or -1 on failure. 246 static void create_vm_process() { 247 pid_t first = clone(nullptr, nullptr, CLONE_FILES, nullptr); 248 if (first == -1) { 249 fatal_errno("failed to clone vm process"); 250 } else if (first == 0) { 251 drop_capabilities(); 252 253 if (clone(nullptr, nullptr, CLONE_FILES, nullptr) == -1) { 254 _exit(errno); 255 } 256 257 // Exit immediately on both sides of the fork. 258 // crash_dump is ptracing us, so it'll get to do whatever it wants in between. 259 _exit(0); 260 } 261 262 int status; 263 if (TEMP_FAILURE_RETRY(waitpid(first, &status, __WCLONE)) != first) { 264 fatal_errno("failed to waitpid in double fork"); 265 } else if (!WIFEXITED(status)) { 266 fatal("intermediate process didn't exit cleanly in double fork (status = %d)", status); 267 } else if (WEXITSTATUS(status)) { 268 fatal("second clone failed: %s", strerror(WEXITSTATUS(status))); 269 } 270 } 271 272 struct debugger_thread_info { 273 pid_t crashing_tid; 274 pid_t pseudothread_tid; 275 siginfo_t* siginfo; 276 void* ucontext; 277 uintptr_t abort_msg; 278 }; 279 280 // Logging and contacting debuggerd requires free file descriptors, which we might not have. 281 // Work around this by spawning a "thread" that shares its parent's address space, but not its file 282 // descriptor table, so that we can close random file descriptors without affecting the original 283 // process. Note that this doesn't go through pthread_create, so TLS is shared with the spawning 284 // process. 285 static void* pseudothread_stack; 286 287 static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) { 288 if (thread_info->siginfo->si_signo == DEBUGGER_SIGNAL && 289 thread_info->siginfo->si_value.sival_int) { 290 return kDebuggerdNativeBacktrace; 291 } 292 293 return kDebuggerdTombstone; 294 } 295 296 static int debuggerd_dispatch_pseudothread(void* arg) { 297 debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg); 298 299 for (int i = 0; i < 1024; ++i) { 300 close(i); 301 } 302 303 int devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)); 304 if (devnull == -1) { 305 fatal_errno("failed to open /dev/null"); 306 } else if (devnull != 0) { 307 fatal_errno("expected /dev/null fd to be 0, actually %d", devnull); 308 } 309 310 // devnull will be 0. 311 TEMP_FAILURE_RETRY(dup2(devnull, 1)); 312 TEMP_FAILURE_RETRY(dup2(devnull, 2)); 313 314 unique_fd input_read, input_write; 315 unique_fd output_read, output_write; 316 if (!Pipe(&input_read, &input_write) != 0 || !Pipe(&output_read, &output_write)) { 317 fatal_errno("failed to create pipe"); 318 } 319 320 // ucontext_t is absurdly large on AArch64, so piece it together manually with writev. 321 uint32_t version = 1; 322 constexpr size_t expected = 323 sizeof(version) + sizeof(siginfo_t) + sizeof(ucontext_t) + sizeof(uintptr_t); 324 325 errno = 0; 326 if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) { 327 fatal_errno("failed to set pipe bufer size"); 328 } 329 330 struct iovec iovs[4] = { 331 {.iov_base = &version, .iov_len = sizeof(version)}, 332 {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)}, 333 {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)}, 334 {.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)}, 335 }; 336 337 ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 4)); 338 if (rc == -1) { 339 fatal_errno("failed to write crash info"); 340 } else if (rc != expected) { 341 fatal("failed to write crash info, wrote %zd bytes, expected %zd", rc, expected); 342 } 343 344 // Don't use fork(2) to avoid calling pthread_atfork handlers. 345 pid_t crash_dump_pid = __fork(); 346 if (crash_dump_pid == -1) { 347 async_safe_format_log(ANDROID_LOG_FATAL, "libc", 348 "failed to fork in debuggerd signal handler: %s", strerror(errno)); 349 } else if (crash_dump_pid == 0) { 350 TEMP_FAILURE_RETRY(dup2(input_write.get(), STDOUT_FILENO)); 351 TEMP_FAILURE_RETRY(dup2(output_read.get(), STDIN_FILENO)); 352 input_read.reset(); 353 input_write.reset(); 354 output_read.reset(); 355 output_write.reset(); 356 357 raise_caps(); 358 359 char main_tid[10]; 360 char pseudothread_tid[10]; 361 char debuggerd_dump_type[10]; 362 async_safe_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid); 363 async_safe_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d", 364 thread_info->pseudothread_tid); 365 async_safe_format_buffer(debuggerd_dump_type, sizeof(debuggerd_dump_type), "%d", 366 get_dump_type(thread_info)); 367 368 execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type, 369 nullptr, nullptr); 370 fatal_errno("exec failed"); 371 } 372 373 input_write.reset(); 374 output_read.reset(); 375 376 // crash_dump will ptrace and pause all of our threads, and then write to the pipe to tell 377 // us to fork off a process to read memory from. 378 char buf[4]; 379 rc = TEMP_FAILURE_RETRY(read(input_read.get(), &buf, sizeof(buf))); 380 if (rc == -1) { 381 async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno)); 382 return 1; 383 } else if (rc == 0) { 384 async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec"); 385 return 1; 386 } else if (rc != 1) { 387 async_safe_format_log(ANDROID_LOG_FATAL, "libc", 388 "read of IPC pipe returned unexpected value: %zd", rc); 389 return 1; 390 } else if (buf[0] != '\1') { 391 async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure"); 392 return 1; 393 } 394 395 // crash_dump is ptracing us, fork off a copy of our address space for it to use. 396 create_vm_process(); 397 398 // Don't leave a zombie child. 399 int status; 400 if (TEMP_FAILURE_RETRY(waitpid(crash_dump_pid, &status, 0)) == -1) { 401 async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s", 402 strerror(errno)); 403 } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) { 404 async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped"); 405 } 406 407 if (thread_info->siginfo->si_signo != DEBUGGER_SIGNAL) { 408 // For crashes, we don't need to minimize pause latency. 409 // Wait for the dump to complete before having the process exit, to avoid being murdered by 410 // ActivityManager or init. 411 TEMP_FAILURE_RETRY(read(input_read, &buf, sizeof(buf))); 412 } 413 414 return 0; 415 } 416 417 static void resend_signal(siginfo_t* info) { 418 // Signals can either be fatal or nonfatal. 419 // For fatal signals, crash_dump will send us the signal we crashed with 420 // before resuming us, so that processes using waitpid on us will see that we 421 // exited with the correct exit status (e.g. so that sh will report 422 // "Segmentation fault" instead of "Killed"). For this to work, we need 423 // to deregister our signal handler for that signal before continuing. 424 if (info->si_signo != DEBUGGER_SIGNAL) { 425 signal(info->si_signo, SIG_DFL); 426 int rc = syscall(SYS_rt_tgsigqueueinfo, __getpid(), __gettid(), info->si_signo, info); 427 if (rc != 0) { 428 fatal_errno("failed to resend signal during crash"); 429 } 430 } 431 } 432 433 // Handler that does crash dumping by forking and doing the processing in the child. 434 // Do this by ptracing the relevant thread, and then execing debuggerd to do the actual dump. 435 static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* context) { 436 // Make sure we don't change the value of errno, in case a signal comes in between the process 437 // making a syscall and checking errno. 438 ErrnoRestorer restorer; 439 440 // It's possible somebody cleared the SA_SIGINFO flag, which would mean 441 // our "info" arg holds an undefined value. 442 if (!have_siginfo(signal_number)) { 443 info = nullptr; 444 } 445 446 struct siginfo si = {}; 447 if (!info) { 448 memset(&si, 0, sizeof(si)); 449 si.si_signo = signal_number; 450 si.si_code = SI_USER; 451 si.si_pid = __getpid(); 452 si.si_uid = getuid(); 453 info = &si; 454 } else if (info->si_code >= 0 || info->si_code == SI_TKILL) { 455 // rt_tgsigqueueinfo(2)'s documentation appears to be incorrect on kernels 456 // that contain commit 66dd34a (3.9+). The manpage claims to only allow 457 // negative si_code values that are not SI_TKILL, but 66dd34a changed the 458 // check to allow all si_code values in calls coming from inside the house. 459 } 460 461 void* abort_message = nullptr; 462 if (signal_number != DEBUGGER_SIGNAL && g_callbacks.get_abort_message) { 463 abort_message = g_callbacks.get_abort_message(); 464 } 465 466 // If sival_int is ~0, it means that the fallback handler has been called 467 // once before and this function is being called again to dump the stack 468 // of a specific thread. It is possible that the prctl call might return 1, 469 // then return 0 in subsequent calls, so check the sival_int to determine if 470 // the fallback handler should be called first. 471 if (info->si_value.sival_int == ~0 || prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) { 472 // This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely, 473 // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing 474 // ANR trace. 475 debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), abort_message); 476 resend_signal(info); 477 return; 478 } 479 480 // Only allow one thread to handle a signal at a time. 481 int ret = pthread_mutex_lock(&crash_mutex); 482 if (ret != 0) { 483 async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret)); 484 return; 485 } 486 487 log_signal_summary(info); 488 489 debugger_thread_info thread_info = { 490 .pseudothread_tid = -1, 491 .crashing_tid = __gettid(), 492 .siginfo = info, 493 .ucontext = context, 494 .abort_msg = reinterpret_cast<uintptr_t>(abort_message), 495 }; 496 497 // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us. 498 int orig_dumpable = prctl(PR_GET_DUMPABLE); 499 if (prctl(PR_SET_DUMPABLE, 1) != 0) { 500 fatal_errno("failed to set dumpable"); 501 } 502 503 // On kernels with yama_ptrace enabled, also allow any process to attach. 504 bool restore_orig_ptracer = true; 505 if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) != 0) { 506 if (errno == EINVAL) { 507 // This kernel does not support PR_SET_PTRACER_ANY, or Yama is not enabled. 508 restore_orig_ptracer = false; 509 } else { 510 fatal_errno("failed to set traceable"); 511 } 512 } 513 514 // Essentially pthread_create without CLONE_FILES, so we still work during file descriptor 515 // exhaustion. 516 pid_t child_pid = 517 clone(debuggerd_dispatch_pseudothread, pseudothread_stack, 518 CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID, 519 &thread_info, nullptr, nullptr, &thread_info.pseudothread_tid); 520 if (child_pid == -1) { 521 fatal_errno("failed to spawn debuggerd dispatch thread"); 522 } 523 524 // Wait for the child to start... 525 futex_wait(&thread_info.pseudothread_tid, -1); 526 527 // and then wait for it to terminate. 528 futex_wait(&thread_info.pseudothread_tid, child_pid); 529 530 // Restore PR_SET_DUMPABLE to its original value. 531 if (prctl(PR_SET_DUMPABLE, orig_dumpable) != 0) { 532 fatal_errno("failed to restore dumpable"); 533 } 534 535 // Restore PR_SET_PTRACER to its original value. 536 if (restore_orig_ptracer && prctl(PR_SET_PTRACER, 0) != 0) { 537 fatal_errno("failed to restore traceable"); 538 } 539 540 if (info->si_signo == DEBUGGER_SIGNAL) { 541 // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from 542 // starting to dump right before our death. 543 pthread_mutex_unlock(&crash_mutex); 544 } else { 545 // Resend the signal, so that either gdb or the parent's waitpid sees it. 546 resend_signal(info); 547 } 548 } 549 550 void debuggerd_init(debuggerd_callbacks_t* callbacks) { 551 if (callbacks) { 552 g_callbacks = *callbacks; 553 } 554 555 void* thread_stack_allocation = 556 mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 557 if (thread_stack_allocation == MAP_FAILED) { 558 fatal_errno("failed to allocate debuggerd thread stack"); 559 } 560 561 char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE; 562 if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) { 563 fatal_errno("failed to mprotect debuggerd thread stack"); 564 } 565 566 // Stack grows negatively, set it to the last byte in the page... 567 stack = (stack + PAGE_SIZE - 1); 568 // and align it. 569 stack -= 15; 570 pseudothread_stack = stack; 571 572 struct sigaction action; 573 memset(&action, 0, sizeof(action)); 574 sigfillset(&action.sa_mask); 575 action.sa_sigaction = debuggerd_signal_handler; 576 action.sa_flags = SA_RESTART | SA_SIGINFO; 577 578 // Use the alternate signal stack if available so we can catch stack overflows. 579 action.sa_flags |= SA_ONSTACK; 580 debuggerd_register_handlers(&action); 581 } 582