Loading debuggerd/debuggerd.cpp +85 −61 Original line number Original line Diff line number Diff line Loading @@ -449,18 +449,11 @@ static bool drop_privileges() { return true; return true; } } static bool fork_signal_sender(int* in_fd, int* out_fd, pid_t* sender_pid, pid_t target_pid) { // Fork a process that listens for signals to send, or 0, to exit. int input_pipe[2]; static bool fork_signal_sender(int* out_fd, pid_t* sender_pid, pid_t target_pid) { int output_pipe[2]; int sfd[2]; if (pipe(input_pipe) != 0) { if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sfd) != 0) { ALOGE("debuggerd: failed to create input pipe for signal sender: %s", strerror(errno)); ALOGE("debuggerd: failed to create socketpair for signal sender: %s", strerror(errno)); return false; } if (pipe(output_pipe) != 0) { close(input_pipe[0]); close(input_pipe[1]); ALOGE("debuggerd: failed to create output pipe for signal sender: %s", strerror(errno)); return false; return false; } } Loading @@ -469,40 +462,42 @@ static bool fork_signal_sender(int* in_fd, int* out_fd, pid_t* sender_pid, pid_t ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno)); ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno)); return false; return false; } else if (fork_pid == 0) { } else if (fork_pid == 0) { close(input_pipe[1]); close(sfd[1]); close(output_pipe[0]); auto wait = [=]() { while (true) { char buf[1]; int signal; if (TEMP_FAILURE_RETRY(read(input_pipe[0], buf, 1)) != 1) { int rc = TEMP_FAILURE_RETRY(read(sfd[0], &signal, sizeof(signal))); ALOGE("debuggerd: signal sender failed to read from pipe"); if (rc < 0) { ALOGE("debuggerd: signal sender failed to read from socket"); kill(target_pid, SIGKILL); exit(1); exit(1); } } else if (rc != sizeof(signal)) { }; ALOGE("debuggerd: signal sender read unexpected number of bytes: %d", rc); auto notify_done = [=]() { kill(target_pid, SIGKILL); if (TEMP_FAILURE_RETRY(write(output_pipe[1], "", 1)) != 1) { ALOGE("debuggerd: signal sender failed to write to pipe"); exit(1); exit(1); } } }; wait(); // Report success after sending a signal, or before exiting. if (kill(target_pid, SIGSTOP) != 0) { int err = 0; ALOGE("debuggerd: failed to stop target '%d': %s", target_pid, strerror(errno)); if (signal != 0) { if (kill(target_pid, signal) != 0) { err = errno; } } } notify_done(); wait(); if (TEMP_FAILURE_RETRY(write(sfd[0], &err, sizeof(err))) < 0) { if (kill(target_pid, SIGCONT) != 0) { ALOGE("debuggerd: signal sender failed to write: %s", strerror(errno)); ALOGE("debuggerd: failed to resume target '%d': %s", target_pid, strerror(errno)); kill(target_pid, SIGKILL); exit(1); } } notify_done(); if (signal == 0) { exit(0); exit(0); } } } else { } else { close(input_pipe[0]); close(sfd[0]); close(output_pipe[1]); *out_fd = sfd[1]; *in_fd = input_pipe[1]; *out_fd = output_pipe[0]; *sender_pid = fork_pid; *sender_pid = fork_pid; return true; return true; } } Loading Loading @@ -588,9 +583,15 @@ static void handle_request(int fd) { // Don't attach to the sibling threads if we want to attach gdb. // Don't attach to the sibling threads if we want to attach gdb. // Supposedly, it makes the process less reliable. // Supposedly, it makes the process less reliable. bool attach_gdb = should_attach_gdb(&request); bool attach_gdb = should_attach_gdb(&request); int signal_in_fd = -1; int signal_fd = -1; int signal_out_fd = -1; pid_t signal_pid = 0; pid_t signal_pid = 0; // Fork a process that stays root, and listens on a pipe to pause and resume the target. if (!fork_signal_sender(&signal_fd, &signal_pid, request.pid)) { ALOGE("debuggerd: failed to fork signal sender"); exit(1); } if (attach_gdb) { if (attach_gdb) { // Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges. // Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges. if (init_getevent() != 0) { if (init_getevent() != 0) { Loading @@ -598,19 +599,21 @@ static void handle_request(int fd) { attach_gdb = false; attach_gdb = false; } } // Fork a process that stays root, and listens on a pipe to pause and resume the target. if (!fork_signal_sender(&signal_in_fd, &signal_out_fd, &signal_pid, request.pid)) { attach_gdb = false; } } } auto notify_signal_sender = [=]() { auto send_signal = [=](int signal) { char buf[1]; int error; if (TEMP_FAILURE_RETRY(write(signal_in_fd, "", 1)) != 1) { if (TEMP_FAILURE_RETRY(write(signal_fd, &signal, sizeof(signal))) < 0) { ALOGE("debuggerd: failed to notify signal process: %s", strerror(errno)); ALOGE("debuggerd: failed to notify signal process: %s", strerror(errno)); } else if (TEMP_FAILURE_RETRY(read(signal_out_fd, buf, 1)) != 1) { return false; } else if (TEMP_FAILURE_RETRY(read(signal_fd, &error, sizeof(error))) < 0) { ALOGE("debuggerd: failed to read response from signal process: %s", strerror(errno)); ALOGE("debuggerd: failed to read response from signal process: %s", strerror(errno)); return false; } else if (error != 0) { errno = error; return false; } } return true; }; }; std::set<pid_t> siblings; std::set<pid_t> siblings; Loading @@ -624,7 +627,11 @@ static void handle_request(int fd) { bool succeeded = false; bool succeeded = false; // Now that we've done everything that requires privileges, we can drop them. // Now that we've done everything that requires privileges, we can drop them. if (drop_privileges()) { if (!drop_privileges()) { ALOGE("debuggerd: failed to drop privileges, exiting"); _exit(1); } succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings); succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings); if (succeeded) { if (succeeded) { if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { Loading @@ -636,7 +643,9 @@ static void handle_request(int fd) { if (attach_gdb) { if (attach_gdb) { // Tell the signal process to send SIGSTOP to the target. // Tell the signal process to send SIGSTOP to the target. notify_signal_sender(); if (!send_signal(SIGSTOP)) { ALOGE("debuggerd: failed to stop process for gdb attach: %s", strerror(errno)); attach_gdb = false; } } } } Loading @@ -648,17 +657,32 @@ static void handle_request(int fd) { ptrace(PTRACE_DETACH, sibling, 0, 0); ptrace(PTRACE_DETACH, sibling, 0, 0); } } // Send the signal back to the process if it crashed and we're not waiting for gdb. if (!attach_gdb && request.action == DEBUGGER_ACTION_CRASH) { // TODO: Send the same signal that triggered the dump, so that shell says "Segmentation fault" // instead of "Killed"? if (!send_signal(SIGKILL)) { ALOGE("debuggerd: failed to kill process %d: %s", request.pid, strerror(errno)); } } // Wait for gdb, if requested. // Wait for gdb, if requested. if (attach_gdb && succeeded) { if (attach_gdb && succeeded) { wait_for_user_action(request); wait_for_user_action(request); // Tell the signal process to send SIGCONT to the target. // Tell the signal process to send SIGCONT to the target. notify_signal_sender(); if (!send_signal(SIGCONT)) { ALOGE("debuggerd: failed to resume process %d: %s", request.pid, strerror(errno)); } uninit_getevent(); uninit_getevent(); waitpid(signal_pid, nullptr, 0); } } if (!send_signal(0)) { ALOGE("debuggerd: failed to notify signal sender to finish"); kill(signal_pid, SIGKILL); } waitpid(signal_pid, nullptr, 0); exit(!succeeded); exit(!succeeded); } } Loading Loading
debuggerd/debuggerd.cpp +85 −61 Original line number Original line Diff line number Diff line Loading @@ -449,18 +449,11 @@ static bool drop_privileges() { return true; return true; } } static bool fork_signal_sender(int* in_fd, int* out_fd, pid_t* sender_pid, pid_t target_pid) { // Fork a process that listens for signals to send, or 0, to exit. int input_pipe[2]; static bool fork_signal_sender(int* out_fd, pid_t* sender_pid, pid_t target_pid) { int output_pipe[2]; int sfd[2]; if (pipe(input_pipe) != 0) { if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sfd) != 0) { ALOGE("debuggerd: failed to create input pipe for signal sender: %s", strerror(errno)); ALOGE("debuggerd: failed to create socketpair for signal sender: %s", strerror(errno)); return false; } if (pipe(output_pipe) != 0) { close(input_pipe[0]); close(input_pipe[1]); ALOGE("debuggerd: failed to create output pipe for signal sender: %s", strerror(errno)); return false; return false; } } Loading @@ -469,40 +462,42 @@ static bool fork_signal_sender(int* in_fd, int* out_fd, pid_t* sender_pid, pid_t ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno)); ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno)); return false; return false; } else if (fork_pid == 0) { } else if (fork_pid == 0) { close(input_pipe[1]); close(sfd[1]); close(output_pipe[0]); auto wait = [=]() { while (true) { char buf[1]; int signal; if (TEMP_FAILURE_RETRY(read(input_pipe[0], buf, 1)) != 1) { int rc = TEMP_FAILURE_RETRY(read(sfd[0], &signal, sizeof(signal))); ALOGE("debuggerd: signal sender failed to read from pipe"); if (rc < 0) { ALOGE("debuggerd: signal sender failed to read from socket"); kill(target_pid, SIGKILL); exit(1); exit(1); } } else if (rc != sizeof(signal)) { }; ALOGE("debuggerd: signal sender read unexpected number of bytes: %d", rc); auto notify_done = [=]() { kill(target_pid, SIGKILL); if (TEMP_FAILURE_RETRY(write(output_pipe[1], "", 1)) != 1) { ALOGE("debuggerd: signal sender failed to write to pipe"); exit(1); exit(1); } } }; wait(); // Report success after sending a signal, or before exiting. if (kill(target_pid, SIGSTOP) != 0) { int err = 0; ALOGE("debuggerd: failed to stop target '%d': %s", target_pid, strerror(errno)); if (signal != 0) { if (kill(target_pid, signal) != 0) { err = errno; } } } notify_done(); wait(); if (TEMP_FAILURE_RETRY(write(sfd[0], &err, sizeof(err))) < 0) { if (kill(target_pid, SIGCONT) != 0) { ALOGE("debuggerd: signal sender failed to write: %s", strerror(errno)); ALOGE("debuggerd: failed to resume target '%d': %s", target_pid, strerror(errno)); kill(target_pid, SIGKILL); exit(1); } } notify_done(); if (signal == 0) { exit(0); exit(0); } } } else { } else { close(input_pipe[0]); close(sfd[0]); close(output_pipe[1]); *out_fd = sfd[1]; *in_fd = input_pipe[1]; *out_fd = output_pipe[0]; *sender_pid = fork_pid; *sender_pid = fork_pid; return true; return true; } } Loading Loading @@ -588,9 +583,15 @@ static void handle_request(int fd) { // Don't attach to the sibling threads if we want to attach gdb. // Don't attach to the sibling threads if we want to attach gdb. // Supposedly, it makes the process less reliable. // Supposedly, it makes the process less reliable. bool attach_gdb = should_attach_gdb(&request); bool attach_gdb = should_attach_gdb(&request); int signal_in_fd = -1; int signal_fd = -1; int signal_out_fd = -1; pid_t signal_pid = 0; pid_t signal_pid = 0; // Fork a process that stays root, and listens on a pipe to pause and resume the target. if (!fork_signal_sender(&signal_fd, &signal_pid, request.pid)) { ALOGE("debuggerd: failed to fork signal sender"); exit(1); } if (attach_gdb) { if (attach_gdb) { // Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges. // Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges. if (init_getevent() != 0) { if (init_getevent() != 0) { Loading @@ -598,19 +599,21 @@ static void handle_request(int fd) { attach_gdb = false; attach_gdb = false; } } // Fork a process that stays root, and listens on a pipe to pause and resume the target. if (!fork_signal_sender(&signal_in_fd, &signal_out_fd, &signal_pid, request.pid)) { attach_gdb = false; } } } auto notify_signal_sender = [=]() { auto send_signal = [=](int signal) { char buf[1]; int error; if (TEMP_FAILURE_RETRY(write(signal_in_fd, "", 1)) != 1) { if (TEMP_FAILURE_RETRY(write(signal_fd, &signal, sizeof(signal))) < 0) { ALOGE("debuggerd: failed to notify signal process: %s", strerror(errno)); ALOGE("debuggerd: failed to notify signal process: %s", strerror(errno)); } else if (TEMP_FAILURE_RETRY(read(signal_out_fd, buf, 1)) != 1) { return false; } else if (TEMP_FAILURE_RETRY(read(signal_fd, &error, sizeof(error))) < 0) { ALOGE("debuggerd: failed to read response from signal process: %s", strerror(errno)); ALOGE("debuggerd: failed to read response from signal process: %s", strerror(errno)); return false; } else if (error != 0) { errno = error; return false; } } return true; }; }; std::set<pid_t> siblings; std::set<pid_t> siblings; Loading @@ -624,7 +627,11 @@ static void handle_request(int fd) { bool succeeded = false; bool succeeded = false; // Now that we've done everything that requires privileges, we can drop them. // Now that we've done everything that requires privileges, we can drop them. if (drop_privileges()) { if (!drop_privileges()) { ALOGE("debuggerd: failed to drop privileges, exiting"); _exit(1); } succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings); succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings); if (succeeded) { if (succeeded) { if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { Loading @@ -636,7 +643,9 @@ static void handle_request(int fd) { if (attach_gdb) { if (attach_gdb) { // Tell the signal process to send SIGSTOP to the target. // Tell the signal process to send SIGSTOP to the target. notify_signal_sender(); if (!send_signal(SIGSTOP)) { ALOGE("debuggerd: failed to stop process for gdb attach: %s", strerror(errno)); attach_gdb = false; } } } } Loading @@ -648,17 +657,32 @@ static void handle_request(int fd) { ptrace(PTRACE_DETACH, sibling, 0, 0); ptrace(PTRACE_DETACH, sibling, 0, 0); } } // Send the signal back to the process if it crashed and we're not waiting for gdb. if (!attach_gdb && request.action == DEBUGGER_ACTION_CRASH) { // TODO: Send the same signal that triggered the dump, so that shell says "Segmentation fault" // instead of "Killed"? if (!send_signal(SIGKILL)) { ALOGE("debuggerd: failed to kill process %d: %s", request.pid, strerror(errno)); } } // Wait for gdb, if requested. // Wait for gdb, if requested. if (attach_gdb && succeeded) { if (attach_gdb && succeeded) { wait_for_user_action(request); wait_for_user_action(request); // Tell the signal process to send SIGCONT to the target. // Tell the signal process to send SIGCONT to the target. notify_signal_sender(); if (!send_signal(SIGCONT)) { ALOGE("debuggerd: failed to resume process %d: %s", request.pid, strerror(errno)); } uninit_getevent(); uninit_getevent(); waitpid(signal_pid, nullptr, 0); } } if (!send_signal(0)) { ALOGE("debuggerd: failed to notify signal sender to finish"); kill(signal_pid, SIGKILL); } waitpid(signal_pid, nullptr, 0); exit(!succeeded); exit(!succeeded); } } Loading