diff options
author | Josh Gao <jmgao@google.com> | 2016-03-16 18:09:15 -0700 |
---|---|---|
committer | Josh Gao <jmgao@google.com> | 2016-03-17 11:27:31 -0700 |
commit | f5e8f0b9cd9ec0214a6f9cd38dd6d9af3268f9aa (patch) | |
tree | 2c155f4acd3cbc3378ed95621048a9457e63f34c | |
parent | 371e7ea170d749489a0eb7085347f58b7be63734 (diff) | |
download | core-f5e8f0b9cd9ec0214a6f9cd38dd6d9af3268f9aa.tar.gz core-f5e8f0b9cd9ec0214a6f9cd38dd6d9af3268f9aa.tar.bz2 core-f5e8f0b9cd9ec0214a6f9cd38dd6d9af3268f9aa.zip |
debuggerd: fork the signal sender once.
Bug: http://b/27427439
Change-Id: I6294ff68a150bc9950a300264c31d2141307ac66
-rw-r--r-- | debuggerd/Android.mk | 1 | ||||
-rw-r--r-- | debuggerd/debuggerd.cpp | 105 | ||||
-rw-r--r-- | debuggerd/signal_sender.cpp | 152 | ||||
-rw-r--r-- | debuggerd/signal_sender.h | 30 |
4 files changed, 197 insertions, 91 deletions
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk index 9e4f1f7d2..6469db451 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -15,6 +15,7 @@ LOCAL_SRC_FILES:= \ debuggerd.cpp \ elf_utils.cpp \ getevent.cpp \ + signal_sender.cpp \ tombstone.cpp \ utility.cpp \ diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp index eabbb9abf..b6c2f8a18 100644 --- a/debuggerd/debuggerd.cpp +++ b/debuggerd/debuggerd.cpp @@ -15,21 +15,20 @@ */ #include <dirent.h> +#include <elf.h> #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <signal.h> #include <stdarg.h> #include <stdio.h> -#include <sys/types.h> -#include <time.h> - -#include <elf.h> #include <sys/poll.h> #include <sys/prctl.h> #include <sys/ptrace.h> #include <sys/stat.h> +#include <sys/types.h> #include <sys/wait.h> +#include <time.h> #include <set> @@ -48,6 +47,7 @@ #include "backtrace.h" #include "getevent.h" +#include "signal_sender.h" #include "tombstone.h" #include "utility.h" @@ -422,7 +422,7 @@ static bool perform_dump(const debugger_request_t& request, int fd, int tombston // this we get a lot of "ptrace detach failed: // No such process". *crash_signal = signal; - kill(request.pid, SIGSTOP); + send_signal(request.pid, 0, SIGSTOP); engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, signal, request.original_si_code, request.abort_msg_address); break; @@ -451,60 +451,6 @@ static bool drop_privileges() { return true; } -// Fork a process that listens for signals to send, or 0, to exit. -static bool fork_signal_sender(int* out_fd, pid_t* sender_pid, pid_t target_pid) { - int sfd[2]; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sfd) != 0) { - ALOGE("debuggerd: failed to create socketpair for signal sender: %s", strerror(errno)); - return false; - } - - pid_t fork_pid = fork(); - if (fork_pid == -1) { - ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno)); - return false; - } else if (fork_pid == 0) { - close(sfd[1]); - - while (true) { - int signal; - int rc = TEMP_FAILURE_RETRY(read(sfd[0], &signal, sizeof(signal))); - if (rc < 0) { - ALOGE("debuggerd: signal sender failed to read from socket"); - kill(target_pid, SIGKILL); - exit(1); - } else if (rc != sizeof(signal)) { - ALOGE("debuggerd: signal sender read unexpected number of bytes: %d", rc); - kill(target_pid, SIGKILL); - exit(1); - } - - // Report success after sending a signal, or before exiting. - int err = 0; - if (signal != 0) { - if (kill(target_pid, signal) != 0) { - err = errno; - } - } - - if (TEMP_FAILURE_RETRY(write(sfd[0], &err, sizeof(err))) < 0) { - ALOGE("debuggerd: signal sender failed to write: %s", strerror(errno)); - kill(target_pid, SIGKILL); - exit(1); - } - - if (signal == 0) { - exit(0); - } - } - } else { - close(sfd[0]); - *out_fd = sfd[1]; - *sender_pid = fork_pid; - return true; - } -} - static void handle_request(int fd) { ALOGV("handle_request(%d)\n", fd); @@ -585,15 +531,6 @@ static void handle_request(int fd) { // Don't attach to the sibling threads if we want to attach gdb. // Supposedly, it makes the process less reliable. bool attach_gdb = should_attach_gdb(&request); - int signal_fd = -1; - 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) { // Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges. if (init_getevent() != 0) { @@ -603,21 +540,6 @@ static void handle_request(int fd) { } - auto send_signal = [=](int signal) { - int error; - if (TEMP_FAILURE_RETRY(write(signal_fd, &signal, sizeof(signal))) < 0) { - ALOGE("debuggerd: failed to notify signal process: %s", strerror(errno)); - 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)); - return false; - } else if (error != 0) { - errno = error; - return false; - } - return true; - }; - std::set<pid_t> siblings; if (!attach_gdb) { ptrace_siblings(request.pid, request.tid, siblings); @@ -646,7 +568,7 @@ static void handle_request(int fd) { if (attach_gdb) { // Tell the signal process to send SIGSTOP to the target. - if (!send_signal(SIGSTOP)) { + if (!send_signal(request.pid, 0, SIGSTOP)) { ALOGE("debuggerd: failed to stop process for gdb attach: %s", strerror(errno)); attach_gdb = false; } @@ -662,7 +584,7 @@ static void handle_request(int fd) { // 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) { - if (!send_signal(crash_signal)) { + if (!send_signal(request.pid, request.tid, crash_signal)) { ALOGE("debuggerd: failed to kill process %d: %s", request.pid, strerror(errno)); } } @@ -672,18 +594,13 @@ static void handle_request(int fd) { wait_for_user_action(request); // Tell the signal process to send SIGCONT to the target. - if (!send_signal(SIGCONT)) { + if (!send_signal(request.pid, 0, SIGCONT)) { ALOGE("debuggerd: failed to resume process %d: %s", request.pid, strerror(errno)); } uninit_getevent(); } - if (!send_signal(0)) { - ALOGE("debuggerd: failed to notify signal sender to finish"); - kill(signal_pid, SIGKILL); - } - waitpid(signal_pid, nullptr, 0); exit(!succeeded); } @@ -714,6 +631,12 @@ static int do_server() { SOCK_STREAM | SOCK_CLOEXEC); if (s == -1) return 1; + // Fork a process that stays root, and listens on a pipe to pause and resume the target. + if (!start_signal_sender()) { + ALOGE("debuggerd: failed to fork signal sender"); + return 1; + } + ALOGI("debuggerd: starting\n"); for (;;) { diff --git a/debuggerd/signal_sender.cpp b/debuggerd/signal_sender.cpp new file mode 100644 index 000000000..35e0c0b57 --- /dev/null +++ b/debuggerd/signal_sender.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <log/logger.h> + +#include "signal_sender.h" + +static int signal_fd = -1; +static pid_t signal_pid; +struct signal_message { + pid_t pid; + pid_t tid; + int signal; +}; + +// Fork a process to send signals for the worker processes to use after they've dropped privileges. +bool start_signal_sender() { + if (signal_pid == 0) { + ALOGE("debuggerd: attempted to start signal sender multiple times"); + return false; + } + + int sfd[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sfd) != 0) { + ALOGE("debuggerd: failed to create socketpair for signal sender: %s", strerror(errno)); + return false; + } + + pid_t parent = getpid(); + pid_t fork_pid = fork(); + if (fork_pid == -1) { + ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno)); + return false; + } else if (fork_pid == 0) { + close(sfd[1]); + + while (true) { + signal_message msg; + int rc = TEMP_FAILURE_RETRY(read(sfd[0], &msg, sizeof(msg))); + if (rc < 0) { + ALOGE("debuggerd: signal sender failed to read from socket"); + break; + } else if (rc != sizeof(msg)) { + ALOGE("debuggerd: signal sender read unexpected number of bytes: %d", rc); + break; + } + + // Report success after sending a signal + int err = 0; + if (msg.tid > 0) { + if (syscall(SYS_tgkill, msg.pid, msg.tid, msg.signal) != 0) { + err = errno; + } + } else { + if (kill(msg.pid, msg.signal) != 0) { + err = errno; + } + } + + if (TEMP_FAILURE_RETRY(write(sfd[0], &err, sizeof(err))) < 0) { + ALOGE("debuggerd: signal sender failed to write: %s", strerror(errno)); + } + } + + // Our parent proably died, but if not, kill them. + if (getppid() == parent) { + kill(parent, SIGKILL); + } + _exit(1); + } else { + close(sfd[0]); + signal_fd = sfd[1]; + signal_pid = fork_pid; + return true; + } +} + +bool stop_signal_sender() { + if (signal_pid <= 0) { + return false; + } + + if (kill(signal_pid, SIGKILL) != 0) { + ALOGE("debuggerd: failed to kill signal sender: %s", strerror(errno)); + return false; + } + + close(signal_fd); + signal_fd = -1; + + int status; + waitpid(signal_pid, &status, 0); + signal_pid = 0; + + return true; +} + +bool send_signal(pid_t pid, pid_t tid, int signal) { + if (signal_fd == -1) { + ALOGE("debuggerd: attempted to send signal before signal sender was started"); + errno = EHOSTUNREACH; + return false; + } + + signal_message msg = {.pid = pid, .tid = tid, .signal = signal }; + if (TEMP_FAILURE_RETRY(write(signal_fd, &msg, sizeof(msg))) < 0) { + ALOGE("debuggerd: failed to send message to signal sender: %s", strerror(errno)); + errno = EHOSTUNREACH; + return false; + } + + int response; + ssize_t rc = TEMP_FAILURE_RETRY(read(signal_fd, &response, sizeof(response))); + if (rc == 0) { + ALOGE("debuggerd: received EOF from signal sender"); + errno = EHOSTUNREACH; + return false; + } else if (rc < 0) { + ALOGE("debuggerd: failed to receive response from signal sender: %s", strerror(errno)); + errno = EHOSTUNREACH; + return false; + } + + if (response == 0) { + return true; + } + + errno = response; + return false; +} diff --git a/debuggerd/signal_sender.h b/debuggerd/signal_sender.h new file mode 100644 index 000000000..0443272e6 --- /dev/null +++ b/debuggerd/signal_sender.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DEBUGGERD_SIGNAL_SENDER_H +#define _DEBUGGERD_SIGNAL_SENDER_H + +#include <sys/types.h> + +bool start_signal_sender(); +bool stop_signal_sender(); + +// Sends a signal to a target process or thread. +// If tid is greater than zero, this performs tgkill(pid, tid, signal). +// Otherwise, it performs kill(pid, signal). +bool send_signal(pid_t pid, pid_t tid, int signal); + +#endif |