Loading debuggerd/crash_dump.cpp +14 −16 Original line number Diff line number Diff line Loading @@ -264,15 +264,13 @@ static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo, ssize_t expected_size = 0; switch (crash_info->header.version) { case 1: expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV1); break; case 2: expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2); case 3: expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic); break; case 3: expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3); case 4: expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic); break; default: Loading @@ -280,25 +278,25 @@ static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo, break; }; if (rc != expected_size) { if (rc < expected_size) { LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected " << expected_size; } } switch (crash_info->header.version) { case 3: process_info->gwp_asan_state = crash_info->data.v3.gwp_asan_state; process_info->gwp_asan_metadata = crash_info->data.v3.gwp_asan_metadata; FALLTHROUGH_INTENDED; case 2: process_info->fdsan_table_address = crash_info->data.v2.fdsan_table_address; case 4: process_info->fdsan_table_address = crash_info->data.d.fdsan_table_address; process_info->gwp_asan_state = crash_info->data.d.gwp_asan_state; process_info->gwp_asan_metadata = crash_info->data.d.gwp_asan_metadata; FALLTHROUGH_INTENDED; case 1: process_info->abort_msg_address = crash_info->data.v1.abort_msg_address; *siginfo = crash_info->data.v1.siginfo; case 2: case 3: process_info->abort_msg_address = crash_info->data.s.abort_msg_address; *siginfo = crash_info->data.s.siginfo; regs->reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), &crash_info->data.v1.ucontext)); &crash_info->data.s.ucontext)); break; default: Loading debuggerd/crasher/crasher.cpp +4 −2 Original line number Diff line number Diff line Loading @@ -349,7 +349,7 @@ noinline int do_action(const char* arg) { int main(int argc, char** argv) { #if defined(STATIC_CRASHER) debuggerd_callbacks_t callbacks = { .get_abort_message = []() { .get_process_info = []() { static struct { size_t size; char msg[32]; Loading @@ -357,7 +357,9 @@ int main(int argc, char** argv) { msg.size = strlen("dummy abort message"); memcpy(msg.msg, "dummy abort message", strlen("dummy abort message")); return reinterpret_cast<abort_msg_t*>(&msg); return debugger_process_info{ .abort_msg = reinterpret_cast<void*>(&msg), }; }, .post_dump = nullptr }; Loading debuggerd/handler/debuggerd_handler.cpp +30 −35 Original line number Diff line number Diff line Loading @@ -297,10 +297,7 @@ struct debugger_thread_info { pid_t pseudothread_tid; siginfo_t* siginfo; void* ucontext; uintptr_t abort_msg; uintptr_t fdsan_table; uintptr_t gwp_asan_state; uintptr_t gwp_asan_metadata; debugger_process_info process_info; }; // Logging and contacting debuggerd requires free file descriptors, which we might not have. Loading Loading @@ -344,25 +341,36 @@ static int debuggerd_dispatch_pseudothread(void* arg) { fatal_errno("failed to create pipe"); } uint32_t version; ssize_t expected; // ucontext_t is absurdly large on AArch64, so piece it together manually with writev. uint32_t version = 3; constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3); struct iovec iovs[4] = { {.iov_base = &version, .iov_len = sizeof(version)}, {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)}, {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)}, }; if (thread_info->process_info.fdsan_table) { // Dynamic executables always use version 4. There is no need to increment the version number if // the format changes, because the sender (linker) and receiver (crash_dump) are version locked. version = 4; expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic); iovs[3] = {.iov_base = &thread_info->process_info, .iov_len = sizeof(thread_info->process_info)}; } else { // Static executables always use version 1. version = 1; expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic); iovs[3] = {.iov_base = &thread_info->process_info.abort_msg, .iov_len = sizeof(uintptr_t)}; } errno = 0; if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) { fatal_errno("failed to set pipe buffer size"); } struct iovec iovs[] = { {.iov_base = &version, .iov_len = sizeof(version)}, {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)}, {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)}, {.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)}, {.iov_base = &thread_info->fdsan_table, .iov_len = sizeof(uintptr_t)}, {.iov_base = &thread_info->gwp_asan_state, .iov_len = sizeof(uintptr_t)}, {.iov_base = &thread_info->gwp_asan_metadata, .iov_len = sizeof(uintptr_t)}, }; ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, arraysize(iovs))); if (rc == -1) { fatal_errno("failed to write crash info"); Loading Loading @@ -489,29 +497,19 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c // check to allow all si_code values in calls coming from inside the house. } void* abort_message = nullptr; const gwp_asan::AllocatorState* gwp_asan_state = nullptr; const gwp_asan::AllocationMetadata* gwp_asan_metadata = nullptr; debugger_process_info process_info = {}; uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr); if (signal_number == BIONIC_SIGNAL_DEBUGGER) { if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) { // Allow for the abort message to be explicitly specified via the sigqueue value. // Keep the bottom bit intact for representing whether we want a backtrace or a tombstone. if (si_val != kDebuggerdFallbackSivalUintptrRequestDump) { abort_message = reinterpret_cast<void*>(si_val & ~1); process_info.abort_msg = reinterpret_cast<void*>(si_val & ~1); info->si_ptr = reinterpret_cast<void*>(si_val & 1); } } } else { if (g_callbacks.get_abort_message) { abort_message = g_callbacks.get_abort_message(); } if (g_callbacks.get_gwp_asan_state) { gwp_asan_state = g_callbacks.get_gwp_asan_state(); } if (g_callbacks.get_gwp_asan_metadata) { gwp_asan_metadata = g_callbacks.get_gwp_asan_metadata(); } } else if (g_callbacks.get_process_info) { process_info = g_callbacks.get_process_info(); } // If sival_int is ~0, it means that the fallback handler has been called Loading @@ -524,7 +522,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c // This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely, // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing // ANR trace. debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), abort_message); debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), process_info.abort_msg); resend_signal(info); return; } Loading @@ -543,10 +541,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c .pseudothread_tid = -1, .siginfo = info, .ucontext = context, .abort_msg = reinterpret_cast<uintptr_t>(abort_message), .fdsan_table = reinterpret_cast<uintptr_t>(android_fdsan_get_fd_table()), .gwp_asan_state = reinterpret_cast<uintptr_t>(gwp_asan_state), .gwp_asan_metadata = reinterpret_cast<uintptr_t>(gwp_asan_metadata), .process_info = process_info, }; // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us. Loading debuggerd/include/debuggerd/handler.h +10 −3 Original line number Diff line number Diff line Loading @@ -33,13 +33,20 @@ struct AllocatorState; struct AllocationMetadata; }; // namespace gwp_asan // When updating this data structure, CrashInfoDataDynamic and the code in // ReadCrashInfo() must also be updated. struct debugger_process_info { void* abort_msg; void* fdsan_table; const gwp_asan::AllocatorState* gwp_asan_state; const gwp_asan::AllocationMetadata* gwp_asan_metadata; }; // These callbacks are called in a signal handler, and thus must be async signal safe. // If null, the callbacks will not be called. typedef struct { struct abort_msg_t* (*get_abort_message)(); debugger_process_info (*get_process_info)(); void (*post_dump)(); const struct gwp_asan::AllocatorState* (*get_gwp_asan_state)(); const struct gwp_asan::AllocationMetadata* (*get_gwp_asan_metadata)(); } debuggerd_callbacks_t; void debuggerd_init(debuggerd_callbacks_t* callbacks); Loading debuggerd/protocol.h +4 −8 Original line number Diff line number Diff line Loading @@ -85,17 +85,14 @@ struct __attribute__((__packed__)) CrashInfoHeader { uint32_t version; }; struct __attribute__((__packed__)) CrashInfoDataV1 { struct __attribute__((__packed__)) CrashInfoDataStatic { siginfo_t siginfo; ucontext_t ucontext; uintptr_t abort_msg_address; }; struct __attribute__((__packed__)) CrashInfoDataV2 : public CrashInfoDataV1 { struct __attribute__((__packed__)) CrashInfoDataDynamic : public CrashInfoDataStatic { uintptr_t fdsan_table_address; }; struct __attribute__((__packed__)) CrashInfoDataV3 : public CrashInfoDataV2 { uintptr_t gwp_asan_state; uintptr_t gwp_asan_metadata; }; Loading @@ -103,8 +100,7 @@ struct __attribute__((__packed__)) CrashInfoDataV3 : public CrashInfoDataV2 { struct __attribute__((__packed__)) CrashInfo { CrashInfoHeader header; union { CrashInfoDataV1 v1; CrashInfoDataV2 v2; CrashInfoDataV3 v3; CrashInfoDataStatic s; CrashInfoDataDynamic d; } data; }; Loading
debuggerd/crash_dump.cpp +14 −16 Original line number Diff line number Diff line Loading @@ -264,15 +264,13 @@ static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo, ssize_t expected_size = 0; switch (crash_info->header.version) { case 1: expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV1); break; case 2: expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2); case 3: expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic); break; case 3: expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3); case 4: expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic); break; default: Loading @@ -280,25 +278,25 @@ static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo, break; }; if (rc != expected_size) { if (rc < expected_size) { LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected " << expected_size; } } switch (crash_info->header.version) { case 3: process_info->gwp_asan_state = crash_info->data.v3.gwp_asan_state; process_info->gwp_asan_metadata = crash_info->data.v3.gwp_asan_metadata; FALLTHROUGH_INTENDED; case 2: process_info->fdsan_table_address = crash_info->data.v2.fdsan_table_address; case 4: process_info->fdsan_table_address = crash_info->data.d.fdsan_table_address; process_info->gwp_asan_state = crash_info->data.d.gwp_asan_state; process_info->gwp_asan_metadata = crash_info->data.d.gwp_asan_metadata; FALLTHROUGH_INTENDED; case 1: process_info->abort_msg_address = crash_info->data.v1.abort_msg_address; *siginfo = crash_info->data.v1.siginfo; case 2: case 3: process_info->abort_msg_address = crash_info->data.s.abort_msg_address; *siginfo = crash_info->data.s.siginfo; regs->reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), &crash_info->data.v1.ucontext)); &crash_info->data.s.ucontext)); break; default: Loading
debuggerd/crasher/crasher.cpp +4 −2 Original line number Diff line number Diff line Loading @@ -349,7 +349,7 @@ noinline int do_action(const char* arg) { int main(int argc, char** argv) { #if defined(STATIC_CRASHER) debuggerd_callbacks_t callbacks = { .get_abort_message = []() { .get_process_info = []() { static struct { size_t size; char msg[32]; Loading @@ -357,7 +357,9 @@ int main(int argc, char** argv) { msg.size = strlen("dummy abort message"); memcpy(msg.msg, "dummy abort message", strlen("dummy abort message")); return reinterpret_cast<abort_msg_t*>(&msg); return debugger_process_info{ .abort_msg = reinterpret_cast<void*>(&msg), }; }, .post_dump = nullptr }; Loading
debuggerd/handler/debuggerd_handler.cpp +30 −35 Original line number Diff line number Diff line Loading @@ -297,10 +297,7 @@ struct debugger_thread_info { pid_t pseudothread_tid; siginfo_t* siginfo; void* ucontext; uintptr_t abort_msg; uintptr_t fdsan_table; uintptr_t gwp_asan_state; uintptr_t gwp_asan_metadata; debugger_process_info process_info; }; // Logging and contacting debuggerd requires free file descriptors, which we might not have. Loading Loading @@ -344,25 +341,36 @@ static int debuggerd_dispatch_pseudothread(void* arg) { fatal_errno("failed to create pipe"); } uint32_t version; ssize_t expected; // ucontext_t is absurdly large on AArch64, so piece it together manually with writev. uint32_t version = 3; constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3); struct iovec iovs[4] = { {.iov_base = &version, .iov_len = sizeof(version)}, {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)}, {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)}, }; if (thread_info->process_info.fdsan_table) { // Dynamic executables always use version 4. There is no need to increment the version number if // the format changes, because the sender (linker) and receiver (crash_dump) are version locked. version = 4; expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic); iovs[3] = {.iov_base = &thread_info->process_info, .iov_len = sizeof(thread_info->process_info)}; } else { // Static executables always use version 1. version = 1; expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic); iovs[3] = {.iov_base = &thread_info->process_info.abort_msg, .iov_len = sizeof(uintptr_t)}; } errno = 0; if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) { fatal_errno("failed to set pipe buffer size"); } struct iovec iovs[] = { {.iov_base = &version, .iov_len = sizeof(version)}, {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)}, {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)}, {.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)}, {.iov_base = &thread_info->fdsan_table, .iov_len = sizeof(uintptr_t)}, {.iov_base = &thread_info->gwp_asan_state, .iov_len = sizeof(uintptr_t)}, {.iov_base = &thread_info->gwp_asan_metadata, .iov_len = sizeof(uintptr_t)}, }; ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, arraysize(iovs))); if (rc == -1) { fatal_errno("failed to write crash info"); Loading Loading @@ -489,29 +497,19 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c // check to allow all si_code values in calls coming from inside the house. } void* abort_message = nullptr; const gwp_asan::AllocatorState* gwp_asan_state = nullptr; const gwp_asan::AllocationMetadata* gwp_asan_metadata = nullptr; debugger_process_info process_info = {}; uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr); if (signal_number == BIONIC_SIGNAL_DEBUGGER) { if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) { // Allow for the abort message to be explicitly specified via the sigqueue value. // Keep the bottom bit intact for representing whether we want a backtrace or a tombstone. if (si_val != kDebuggerdFallbackSivalUintptrRequestDump) { abort_message = reinterpret_cast<void*>(si_val & ~1); process_info.abort_msg = reinterpret_cast<void*>(si_val & ~1); info->si_ptr = reinterpret_cast<void*>(si_val & 1); } } } else { if (g_callbacks.get_abort_message) { abort_message = g_callbacks.get_abort_message(); } if (g_callbacks.get_gwp_asan_state) { gwp_asan_state = g_callbacks.get_gwp_asan_state(); } if (g_callbacks.get_gwp_asan_metadata) { gwp_asan_metadata = g_callbacks.get_gwp_asan_metadata(); } } else if (g_callbacks.get_process_info) { process_info = g_callbacks.get_process_info(); } // If sival_int is ~0, it means that the fallback handler has been called Loading @@ -524,7 +522,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c // This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely, // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing // ANR trace. debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), abort_message); debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), process_info.abort_msg); resend_signal(info); return; } Loading @@ -543,10 +541,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c .pseudothread_tid = -1, .siginfo = info, .ucontext = context, .abort_msg = reinterpret_cast<uintptr_t>(abort_message), .fdsan_table = reinterpret_cast<uintptr_t>(android_fdsan_get_fd_table()), .gwp_asan_state = reinterpret_cast<uintptr_t>(gwp_asan_state), .gwp_asan_metadata = reinterpret_cast<uintptr_t>(gwp_asan_metadata), .process_info = process_info, }; // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us. Loading
debuggerd/include/debuggerd/handler.h +10 −3 Original line number Diff line number Diff line Loading @@ -33,13 +33,20 @@ struct AllocatorState; struct AllocationMetadata; }; // namespace gwp_asan // When updating this data structure, CrashInfoDataDynamic and the code in // ReadCrashInfo() must also be updated. struct debugger_process_info { void* abort_msg; void* fdsan_table; const gwp_asan::AllocatorState* gwp_asan_state; const gwp_asan::AllocationMetadata* gwp_asan_metadata; }; // These callbacks are called in a signal handler, and thus must be async signal safe. // If null, the callbacks will not be called. typedef struct { struct abort_msg_t* (*get_abort_message)(); debugger_process_info (*get_process_info)(); void (*post_dump)(); const struct gwp_asan::AllocatorState* (*get_gwp_asan_state)(); const struct gwp_asan::AllocationMetadata* (*get_gwp_asan_metadata)(); } debuggerd_callbacks_t; void debuggerd_init(debuggerd_callbacks_t* callbacks); Loading
debuggerd/protocol.h +4 −8 Original line number Diff line number Diff line Loading @@ -85,17 +85,14 @@ struct __attribute__((__packed__)) CrashInfoHeader { uint32_t version; }; struct __attribute__((__packed__)) CrashInfoDataV1 { struct __attribute__((__packed__)) CrashInfoDataStatic { siginfo_t siginfo; ucontext_t ucontext; uintptr_t abort_msg_address; }; struct __attribute__((__packed__)) CrashInfoDataV2 : public CrashInfoDataV1 { struct __attribute__((__packed__)) CrashInfoDataDynamic : public CrashInfoDataStatic { uintptr_t fdsan_table_address; }; struct __attribute__((__packed__)) CrashInfoDataV3 : public CrashInfoDataV2 { uintptr_t gwp_asan_state; uintptr_t gwp_asan_metadata; }; Loading @@ -103,8 +100,7 @@ struct __attribute__((__packed__)) CrashInfoDataV3 : public CrashInfoDataV2 { struct __attribute__((__packed__)) CrashInfo { CrashInfoHeader header; union { CrashInfoDataV1 v1; CrashInfoDataV2 v2; CrashInfoDataV3 v3; CrashInfoDataStatic s; CrashInfoDataDynamic d; } data; };