Loading debuggerd/client/debuggerd_client.cpp +75 −9 Original line number Original line Diff line number Diff line Loading @@ -31,6 +31,7 @@ #include <errno.h> #include <errno.h> #include <inttypes.h> #include <inttypes.h> #include <pthread.h> #include <pthread.h> #include <sched.h> #include <signal.h> #include <signal.h> #include <stddef.h> #include <stddef.h> #include <stdio.h> #include <stdio.h> Loading @@ -41,6 +42,7 @@ #include <sys/socket.h> #include <sys/socket.h> #include <sys/syscall.h> #include <sys/syscall.h> #include <sys/un.h> #include <sys/un.h> #include <sys/wait.h> #include <unistd.h> #include <unistd.h> #include "private/libc_logging.h" #include "private/libc_logging.h" Loading @@ -56,6 +58,13 @@ static debuggerd_callbacks_t g_callbacks; static debuggerd_callbacks_t g_callbacks; // Don't use __libc_fatal because it exits via abort, which might put us back into a signal handler. #define fatal(...) \ do { \ __libc_format_log(ANDROID_LOG_FATAL, "libc", __VA_ARGS__); \ _exit(1); \ } while (0) static int socket_abstract_client(const char* name, int type) { static int socket_abstract_client(const char* name, int type) { sockaddr_un addr; sockaddr_un addr; Loading Loading @@ -188,7 +197,7 @@ static bool have_siginfo(int signum) { return result; return result; } } static void send_debuggerd_packet() { static void send_debuggerd_packet(pid_t crashing_tid, pid_t pseudothread_tid) { // Mutex to prevent multiple crashing threads from trying to talk // Mutex to prevent multiple crashing threads from trying to talk // to debuggerd at the same time. // to debuggerd at the same time. static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER; Loading Loading @@ -218,7 +227,8 @@ static void send_debuggerd_packet() { // that's actually in our process. // that's actually in our process. debugger_msg_t msg; debugger_msg_t msg; msg.action = DEBUGGER_ACTION_CRASH; msg.action = DEBUGGER_ACTION_CRASH; msg.tid = gettid(); msg.tid = crashing_tid; msg.ignore_tid = pseudothread_tid; msg.abort_msg_address = 0; msg.abort_msg_address = 0; if (g_callbacks.get_abort_message) { if (g_callbacks.get_abort_message) { Loading @@ -229,11 +239,9 @@ static void send_debuggerd_packet() { if (ret == sizeof(msg)) { if (ret == sizeof(msg)) { char debuggerd_ack; char debuggerd_ack; ret = TEMP_FAILURE_RETRY(read(s, &debuggerd_ack, 1)); ret = TEMP_FAILURE_RETRY(read(s, &debuggerd_ack, 1)); int saved_errno = errno; if (g_callbacks.post_dump) { if (g_callbacks.post_dump) { g_callbacks.post_dump(); g_callbacks.post_dump(); } } errno = saved_errno; } else { } else { // read or write failed -- broken connection? // read or write failed -- broken connection? __libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed while talking to debuggerd: %s", __libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed while talking to debuggerd: %s", Loading @@ -243,6 +251,33 @@ static void send_debuggerd_packet() { close(s); close(s); } } struct debugger_thread_info { pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pid_t crashing_tid; pid_t pseudothread_tid; int signal_number; siginfo_t* info; }; // Logging and contacting debuggerd requires free file descriptors, which we might not have. // Work around this by spawning a "thread" that shares its parent's address space, but not its file // descriptor table, so that we can close random file descriptors without affecting the original // process. Note that this doesn't go through pthread_create, so TLS is shared with the spawning // process. static void* pseudothread_stack; static int debuggerd_dispatch_pseudothread(void* arg) { debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg); for (int i = 3; i < 1024; ++i) { close(i); } log_signal_summary(thread_info->signal_number, thread_info->info); send_debuggerd_packet(thread_info->crashing_tid, thread_info->pseudothread_tid); pthread_mutex_unlock(&thread_info->mutex); return 0; } /* /* * Catches fatal signals so we can ask debuggerd to ptrace us before * Catches fatal signals so we can ask debuggerd to ptrace us before * we crash. * we crash. Loading @@ -254,9 +289,25 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) info = nullptr; info = nullptr; } } log_signal_summary(signal_number, info); debugger_thread_info thread_info = { .crashing_tid = gettid(), .signal_number = signal_number, .info = info }; pthread_mutex_lock(&thread_info.mutex); pid_t child_pid = clone(debuggerd_dispatch_pseudothread, pseudothread_stack, CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID, &thread_info, nullptr, nullptr, &thread_info.pseudothread_tid); if (child_pid == -1) { fatal("failed to spawn debuggerd dispatch thread: %s", strerror(errno)); } // Wait for the child to finish and unlock the mutex. // This relies on bionic behavior that isn't guaranteed by the standard. pthread_mutex_lock(&thread_info.mutex); send_debuggerd_packet(); // We need to return from the signal handler so that debuggerd can dump the // We need to return from the signal handler so that debuggerd can dump the // thread that crashed, but returning here does not guarantee that the signal // thread that crashed, but returning here does not guarantee that the signal Loading @@ -281,9 +332,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), signal_number, info); int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), signal_number, info); if (rc != 0) { if (rc != 0) { __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to resend signal during crash: %s", fatal("failed to resend signal during crash: %s", strerror(errno)); strerror(errno)); _exit(0); } } } } Loading @@ -292,6 +341,23 @@ void debuggerd_init(debuggerd_callbacks_t* callbacks) { g_callbacks = *callbacks; g_callbacks = *callbacks; } } void* thread_stack_allocation = mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (thread_stack_allocation == MAP_FAILED) { fatal("failed to allocate debuggerd thread stack"); } char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE; if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) { fatal("failed to mprotect debuggerd thread stack"); } // Stack grows negatively, set it to the last byte in the page... stack = (stack + PAGE_SIZE - 1); // and align it. stack -= 15; pseudothread_stack = stack; struct sigaction action; struct sigaction action; memset(&action, 0, sizeof(action)); memset(&action, 0, sizeof(action)); sigemptyset(&action.sa_mask); sigemptyset(&action.sa_mask); Loading debuggerd/crasher.cpp +10 −1 Original line number Original line Diff line number Diff line #include <assert.h> #include <assert.h> #include <errno.h> #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <pthread.h> #include <sched.h> #include <sched.h> #include <signal.h> #include <signal.h> Loading Loading @@ -141,7 +142,13 @@ static int do_action(const char* arg) { { fprintf(stderr, "%s: init pid=%d tid=%d\n", __progname, getpid(), gettid()); fprintf(stderr, "%s: init pid=%d tid=%d\n", __progname, getpid(), gettid()); if (!strncmp(arg, "thread-", strlen("thread-"))) { if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) { errno = 0; while (errno != EMFILE) { open("/dev/null", O_RDONLY); } return do_action(arg + strlen("exhaustfd-")); } else if (!strncmp(arg, "thread-", strlen("thread-"))) { return do_action_on_thread(arg + strlen("thread-")); return do_action_on_thread(arg + strlen("thread-")); } else if (!strcmp(arg, "SIGSEGV-non-null")) { } else if (!strcmp(arg, "SIGSEGV-non-null")) { sigsegv_non_null(); sigsegv_non_null(); Loading Loading @@ -208,6 +215,8 @@ static int do_action(const char* arg) fprintf(stderr, " SIGTRAP cause a SIGTRAP\n"); fprintf(stderr, " SIGTRAP cause a SIGTRAP\n"); fprintf(stderr, "prefix any of the above with 'thread-' to not run\n"); fprintf(stderr, "prefix any of the above with 'thread-' to not run\n"); fprintf(stderr, "on the process' main thread.\n"); fprintf(stderr, "on the process' main thread.\n"); fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n"); fprintf(stderr, "all available file descriptors before crashing.\n"); return EXIT_SUCCESS; return EXIT_SUCCESS; } } Loading debuggerd/debuggerd.cpp +5 −3 Original line number Original line Diff line number Diff line Loading @@ -69,6 +69,7 @@ struct debugger_request_t { debugger_action_t action; debugger_action_t action; pid_t pid, tid; pid_t pid, tid; uid_t uid, gid; uid_t uid, gid; pid_t ignore_tid; uintptr_t abort_msg_address; uintptr_t abort_msg_address; }; }; Loading Loading @@ -225,6 +226,7 @@ static int read_request(int fd, debugger_request_t* out_request) { out_request->action = static_cast<debugger_action_t>(msg.action); out_request->action = static_cast<debugger_action_t>(msg.action); out_request->tid = msg.tid; out_request->tid = msg.tid; out_request->ignore_tid = msg.ignore_tid; out_request->pid = cr.pid; out_request->pid = cr.pid; out_request->uid = cr.uid; out_request->uid = cr.uid; out_request->gid = cr.gid; out_request->gid = cr.gid; Loading Loading @@ -433,7 +435,7 @@ static bool ptrace_attach_thread(pid_t pid, pid_t tid) { return true; return true; } } static void ptrace_siblings(pid_t pid, pid_t main_tid, std::set<pid_t>& tids) { static void ptrace_siblings(pid_t pid, pid_t main_tid, pid_t ignore_tid, std::set<pid_t>& tids) { char task_path[PATH_MAX]; char task_path[PATH_MAX]; if (snprintf(task_path, PATH_MAX, "/proc/%d/task", pid) >= PATH_MAX) { if (snprintf(task_path, PATH_MAX, "/proc/%d/task", pid) >= PATH_MAX) { Loading Loading @@ -462,7 +464,7 @@ static void ptrace_siblings(pid_t pid, pid_t main_tid, std::set<pid_t>& tids) { continue; continue; } } if (tid == main_tid) { if (tid == main_tid || tid == ignore_tid) { continue; continue; } } Loading Loading @@ -633,7 +635,7 @@ static void worker_process(int fd, debugger_request_t& request) { std::set<pid_t> siblings; std::set<pid_t> siblings; if (!attach_gdb) { if (!attach_gdb) { ptrace_siblings(request.pid, request.tid, siblings); ptrace_siblings(request.pid, request.tid, request.ignore_tid, siblings); } } // Generate the backtrace map before dropping privileges. // Generate the backtrace map before dropping privileges. Loading debuggerd/include/debuggerd/client.h +1 −0 Original line number Original line Diff line number Diff line Loading @@ -43,6 +43,7 @@ typedef enum { typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) { int32_t action; int32_t action; pid_t tid; pid_t tid; pid_t ignore_tid; uint64_t abort_msg_address; uint64_t abort_msg_address; } debugger_msg_t; } debugger_msg_t; Loading Loading
debuggerd/client/debuggerd_client.cpp +75 −9 Original line number Original line Diff line number Diff line Loading @@ -31,6 +31,7 @@ #include <errno.h> #include <errno.h> #include <inttypes.h> #include <inttypes.h> #include <pthread.h> #include <pthread.h> #include <sched.h> #include <signal.h> #include <signal.h> #include <stddef.h> #include <stddef.h> #include <stdio.h> #include <stdio.h> Loading @@ -41,6 +42,7 @@ #include <sys/socket.h> #include <sys/socket.h> #include <sys/syscall.h> #include <sys/syscall.h> #include <sys/un.h> #include <sys/un.h> #include <sys/wait.h> #include <unistd.h> #include <unistd.h> #include "private/libc_logging.h" #include "private/libc_logging.h" Loading @@ -56,6 +58,13 @@ static debuggerd_callbacks_t g_callbacks; static debuggerd_callbacks_t g_callbacks; // Don't use __libc_fatal because it exits via abort, which might put us back into a signal handler. #define fatal(...) \ do { \ __libc_format_log(ANDROID_LOG_FATAL, "libc", __VA_ARGS__); \ _exit(1); \ } while (0) static int socket_abstract_client(const char* name, int type) { static int socket_abstract_client(const char* name, int type) { sockaddr_un addr; sockaddr_un addr; Loading Loading @@ -188,7 +197,7 @@ static bool have_siginfo(int signum) { return result; return result; } } static void send_debuggerd_packet() { static void send_debuggerd_packet(pid_t crashing_tid, pid_t pseudothread_tid) { // Mutex to prevent multiple crashing threads from trying to talk // Mutex to prevent multiple crashing threads from trying to talk // to debuggerd at the same time. // to debuggerd at the same time. static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER; Loading Loading @@ -218,7 +227,8 @@ static void send_debuggerd_packet() { // that's actually in our process. // that's actually in our process. debugger_msg_t msg; debugger_msg_t msg; msg.action = DEBUGGER_ACTION_CRASH; msg.action = DEBUGGER_ACTION_CRASH; msg.tid = gettid(); msg.tid = crashing_tid; msg.ignore_tid = pseudothread_tid; msg.abort_msg_address = 0; msg.abort_msg_address = 0; if (g_callbacks.get_abort_message) { if (g_callbacks.get_abort_message) { Loading @@ -229,11 +239,9 @@ static void send_debuggerd_packet() { if (ret == sizeof(msg)) { if (ret == sizeof(msg)) { char debuggerd_ack; char debuggerd_ack; ret = TEMP_FAILURE_RETRY(read(s, &debuggerd_ack, 1)); ret = TEMP_FAILURE_RETRY(read(s, &debuggerd_ack, 1)); int saved_errno = errno; if (g_callbacks.post_dump) { if (g_callbacks.post_dump) { g_callbacks.post_dump(); g_callbacks.post_dump(); } } errno = saved_errno; } else { } else { // read or write failed -- broken connection? // read or write failed -- broken connection? __libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed while talking to debuggerd: %s", __libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed while talking to debuggerd: %s", Loading @@ -243,6 +251,33 @@ static void send_debuggerd_packet() { close(s); close(s); } } struct debugger_thread_info { pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pid_t crashing_tid; pid_t pseudothread_tid; int signal_number; siginfo_t* info; }; // Logging and contacting debuggerd requires free file descriptors, which we might not have. // Work around this by spawning a "thread" that shares its parent's address space, but not its file // descriptor table, so that we can close random file descriptors without affecting the original // process. Note that this doesn't go through pthread_create, so TLS is shared with the spawning // process. static void* pseudothread_stack; static int debuggerd_dispatch_pseudothread(void* arg) { debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg); for (int i = 3; i < 1024; ++i) { close(i); } log_signal_summary(thread_info->signal_number, thread_info->info); send_debuggerd_packet(thread_info->crashing_tid, thread_info->pseudothread_tid); pthread_mutex_unlock(&thread_info->mutex); return 0; } /* /* * Catches fatal signals so we can ask debuggerd to ptrace us before * Catches fatal signals so we can ask debuggerd to ptrace us before * we crash. * we crash. Loading @@ -254,9 +289,25 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) info = nullptr; info = nullptr; } } log_signal_summary(signal_number, info); debugger_thread_info thread_info = { .crashing_tid = gettid(), .signal_number = signal_number, .info = info }; pthread_mutex_lock(&thread_info.mutex); pid_t child_pid = clone(debuggerd_dispatch_pseudothread, pseudothread_stack, CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID, &thread_info, nullptr, nullptr, &thread_info.pseudothread_tid); if (child_pid == -1) { fatal("failed to spawn debuggerd dispatch thread: %s", strerror(errno)); } // Wait for the child to finish and unlock the mutex. // This relies on bionic behavior that isn't guaranteed by the standard. pthread_mutex_lock(&thread_info.mutex); send_debuggerd_packet(); // We need to return from the signal handler so that debuggerd can dump the // We need to return from the signal handler so that debuggerd can dump the // thread that crashed, but returning here does not guarantee that the signal // thread that crashed, but returning here does not guarantee that the signal Loading @@ -281,9 +332,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), signal_number, info); int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), signal_number, info); if (rc != 0) { if (rc != 0) { __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to resend signal during crash: %s", fatal("failed to resend signal during crash: %s", strerror(errno)); strerror(errno)); _exit(0); } } } } Loading @@ -292,6 +341,23 @@ void debuggerd_init(debuggerd_callbacks_t* callbacks) { g_callbacks = *callbacks; g_callbacks = *callbacks; } } void* thread_stack_allocation = mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (thread_stack_allocation == MAP_FAILED) { fatal("failed to allocate debuggerd thread stack"); } char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE; if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) { fatal("failed to mprotect debuggerd thread stack"); } // Stack grows negatively, set it to the last byte in the page... stack = (stack + PAGE_SIZE - 1); // and align it. stack -= 15; pseudothread_stack = stack; struct sigaction action; struct sigaction action; memset(&action, 0, sizeof(action)); memset(&action, 0, sizeof(action)); sigemptyset(&action.sa_mask); sigemptyset(&action.sa_mask); Loading
debuggerd/crasher.cpp +10 −1 Original line number Original line Diff line number Diff line #include <assert.h> #include <assert.h> #include <errno.h> #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <pthread.h> #include <sched.h> #include <sched.h> #include <signal.h> #include <signal.h> Loading Loading @@ -141,7 +142,13 @@ static int do_action(const char* arg) { { fprintf(stderr, "%s: init pid=%d tid=%d\n", __progname, getpid(), gettid()); fprintf(stderr, "%s: init pid=%d tid=%d\n", __progname, getpid(), gettid()); if (!strncmp(arg, "thread-", strlen("thread-"))) { if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) { errno = 0; while (errno != EMFILE) { open("/dev/null", O_RDONLY); } return do_action(arg + strlen("exhaustfd-")); } else if (!strncmp(arg, "thread-", strlen("thread-"))) { return do_action_on_thread(arg + strlen("thread-")); return do_action_on_thread(arg + strlen("thread-")); } else if (!strcmp(arg, "SIGSEGV-non-null")) { } else if (!strcmp(arg, "SIGSEGV-non-null")) { sigsegv_non_null(); sigsegv_non_null(); Loading Loading @@ -208,6 +215,8 @@ static int do_action(const char* arg) fprintf(stderr, " SIGTRAP cause a SIGTRAP\n"); fprintf(stderr, " SIGTRAP cause a SIGTRAP\n"); fprintf(stderr, "prefix any of the above with 'thread-' to not run\n"); fprintf(stderr, "prefix any of the above with 'thread-' to not run\n"); fprintf(stderr, "on the process' main thread.\n"); fprintf(stderr, "on the process' main thread.\n"); fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n"); fprintf(stderr, "all available file descriptors before crashing.\n"); return EXIT_SUCCESS; return EXIT_SUCCESS; } } Loading
debuggerd/debuggerd.cpp +5 −3 Original line number Original line Diff line number Diff line Loading @@ -69,6 +69,7 @@ struct debugger_request_t { debugger_action_t action; debugger_action_t action; pid_t pid, tid; pid_t pid, tid; uid_t uid, gid; uid_t uid, gid; pid_t ignore_tid; uintptr_t abort_msg_address; uintptr_t abort_msg_address; }; }; Loading Loading @@ -225,6 +226,7 @@ static int read_request(int fd, debugger_request_t* out_request) { out_request->action = static_cast<debugger_action_t>(msg.action); out_request->action = static_cast<debugger_action_t>(msg.action); out_request->tid = msg.tid; out_request->tid = msg.tid; out_request->ignore_tid = msg.ignore_tid; out_request->pid = cr.pid; out_request->pid = cr.pid; out_request->uid = cr.uid; out_request->uid = cr.uid; out_request->gid = cr.gid; out_request->gid = cr.gid; Loading Loading @@ -433,7 +435,7 @@ static bool ptrace_attach_thread(pid_t pid, pid_t tid) { return true; return true; } } static void ptrace_siblings(pid_t pid, pid_t main_tid, std::set<pid_t>& tids) { static void ptrace_siblings(pid_t pid, pid_t main_tid, pid_t ignore_tid, std::set<pid_t>& tids) { char task_path[PATH_MAX]; char task_path[PATH_MAX]; if (snprintf(task_path, PATH_MAX, "/proc/%d/task", pid) >= PATH_MAX) { if (snprintf(task_path, PATH_MAX, "/proc/%d/task", pid) >= PATH_MAX) { Loading Loading @@ -462,7 +464,7 @@ static void ptrace_siblings(pid_t pid, pid_t main_tid, std::set<pid_t>& tids) { continue; continue; } } if (tid == main_tid) { if (tid == main_tid || tid == ignore_tid) { continue; continue; } } Loading Loading @@ -633,7 +635,7 @@ static void worker_process(int fd, debugger_request_t& request) { std::set<pid_t> siblings; std::set<pid_t> siblings; if (!attach_gdb) { if (!attach_gdb) { ptrace_siblings(request.pid, request.tid, siblings); ptrace_siblings(request.pid, request.tid, request.ignore_tid, siblings); } } // Generate the backtrace map before dropping privileges. // Generate the backtrace map before dropping privileges. Loading
debuggerd/include/debuggerd/client.h +1 −0 Original line number Original line Diff line number Diff line Loading @@ -43,6 +43,7 @@ typedef enum { typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) { int32_t action; int32_t action; pid_t tid; pid_t tid; pid_t ignore_tid; uint64_t abort_msg_address; uint64_t abort_msg_address; } debugger_msg_t; } debugger_msg_t; Loading