Merge commit 'd3896c106d6a6e4d461be658a9e66efbde31f28d' into nyc-dev-plus-aosp
Change-Id: I77d77be5ba21c55cdf1172d7a7f7b4ba04dcbc81
This commit is contained in:
commit
e758b2b3d3
4 changed files with 91 additions and 13 deletions
|
|
@ -31,6 +31,7 @@
|
|||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -41,6 +42,7 @@
|
|||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "private/libc_logging.h"
|
||||
|
|
@ -56,6 +58,13 @@
|
|||
|
||||
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) {
|
||||
sockaddr_un addr;
|
||||
|
||||
|
|
@ -188,7 +197,7 @@ static bool have_siginfo(int signum) {
|
|||
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
|
||||
// to debuggerd at the same time.
|
||||
static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
|
@ -218,7 +227,8 @@ static void send_debuggerd_packet() {
|
|||
// that's actually in our process.
|
||||
debugger_msg_t msg;
|
||||
msg.action = DEBUGGER_ACTION_CRASH;
|
||||
msg.tid = gettid();
|
||||
msg.tid = crashing_tid;
|
||||
msg.ignore_tid = pseudothread_tid;
|
||||
msg.abort_msg_address = 0;
|
||||
|
||||
if (g_callbacks.get_abort_message) {
|
||||
|
|
@ -229,11 +239,9 @@ static void send_debuggerd_packet() {
|
|||
if (ret == sizeof(msg)) {
|
||||
char debuggerd_ack;
|
||||
ret = TEMP_FAILURE_RETRY(read(s, &debuggerd_ack, 1));
|
||||
int saved_errno = errno;
|
||||
if (g_callbacks.post_dump) {
|
||||
g_callbacks.post_dump();
|
||||
}
|
||||
errno = saved_errno;
|
||||
} else {
|
||||
// read or write failed -- broken connection?
|
||||
__libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed while talking to debuggerd: %s",
|
||||
|
|
@ -243,6 +251,33 @@ static void send_debuggerd_packet() {
|
|||
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
|
||||
* we crash.
|
||||
|
|
@ -254,9 +289,25 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*)
|
|||
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
|
||||
// thread that crashed, but returning here does not guarantee that the signal
|
||||
|
|
@ -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);
|
||||
if (rc != 0) {
|
||||
__libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to resend signal during crash: %s",
|
||||
strerror(errno));
|
||||
_exit(0);
|
||||
fatal("failed to resend signal during crash: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -292,6 +341,23 @@ void debuggerd_init(debuggerd_callbacks_t* 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;
|
||||
memset(&action, 0, sizeof(action));
|
||||
sigemptyset(&action.sa_mask);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
|
|
@ -141,7 +142,13 @@ static int do_action(const char* arg)
|
|||
{
|
||||
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-"));
|
||||
} else if (!strcmp(arg, "SIGSEGV-non-null")) {
|
||||
sigsegv_non_null();
|
||||
|
|
@ -208,6 +215,8 @@ static int do_action(const char* arg)
|
|||
fprintf(stderr, " SIGTRAP cause a SIGTRAP\n");
|
||||
fprintf(stderr, "prefix any of the above with 'thread-' to not run\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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ struct debugger_request_t {
|
|||
debugger_action_t action;
|
||||
pid_t pid, tid;
|
||||
uid_t uid, gid;
|
||||
pid_t ignore_tid;
|
||||
uintptr_t abort_msg_address;
|
||||
};
|
||||
|
||||
|
|
@ -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->tid = msg.tid;
|
||||
out_request->ignore_tid = msg.ignore_tid;
|
||||
out_request->pid = cr.pid;
|
||||
out_request->uid = cr.uid;
|
||||
out_request->gid = cr.gid;
|
||||
|
|
@ -433,7 +435,7 @@ static bool ptrace_attach_thread(pid_t pid, pid_t tid) {
|
|||
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];
|
||||
|
||||
if (snprintf(task_path, PATH_MAX, "/proc/%d/task", pid) >= PATH_MAX) {
|
||||
|
|
@ -462,7 +464,7 @@ static void ptrace_siblings(pid_t pid, pid_t main_tid, std::set<pid_t>& tids) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (tid == main_tid) {
|
||||
if (tid == main_tid || tid == ignore_tid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -633,7 +635,7 @@ static void worker_process(int fd, debugger_request_t& request) {
|
|||
|
||||
std::set<pid_t> siblings;
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ typedef enum {
|
|||
typedef struct __attribute__((packed)) {
|
||||
int32_t action;
|
||||
pid_t tid;
|
||||
pid_t ignore_tid;
|
||||
uint64_t abort_msg_address;
|
||||
} debugger_msg_t;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue