diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp index 087ebaea8..0cd2350a8 100644 --- a/debuggerd/crash_dump.cpp +++ b/debuggerd/crash_dump.cpp @@ -295,6 +295,13 @@ static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo, case 3: process_info->abort_msg_address = crash_info->data.s.abort_msg_address; *siginfo = crash_info->data.s.siginfo; + if (signal_has_si_addr(siginfo)) { + // Make a copy of the ucontext field because otherwise it is not aligned enough (due to + // being in a packed struct) and clang complains about that. + ucontext_t ucontext = crash_info->data.s.ucontext; + process_info->has_fault_address = true; + process_info->fault_address = get_fault_address(siginfo, &ucontext); + } regs->reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), &crash_info->data.s.ucontext)); break; diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp index 6a8cc563d..054f83675 100644 --- a/debuggerd/debuggerd_test.cpp +++ b/debuggerd/debuggerd_test.cpp @@ -305,6 +305,32 @@ TEST_F(CrasherTest, smoke) { ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)"); } +TEST_F(CrasherTest, tagged_fault_addr) { +#if !defined(__aarch64__) + GTEST_SKIP() << "Requires aarch64"; +#endif + int intercept_result; + unique_fd output_fd; + StartProcess([]() { + *reinterpret_cast(0x100000000000dead) = '1'; + }); + + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGSEGV); + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + + // The address can either be tagged (new kernels) or untagged (old kernels). + ASSERT_MATCH( + result, + R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr (0x100000000000dead|0xdead))"); +} + TEST_F(CrasherTest, LD_PRELOAD) { int intercept_result; unique_fd output_fd; diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp index 84d2bd09a..003c3d9a6 100644 --- a/debuggerd/handler/debuggerd_handler.cpp +++ b/debuggerd/handler/debuggerd_handler.cpp @@ -167,7 +167,7 @@ static bool get_main_thread_name(char* buf, size_t len) { * mutex is being held, so we don't want to use any libc functions that * could allocate memory or hold a lock. */ -static void log_signal_summary(const siginfo_t* info) { +static void log_signal_summary(const siginfo_t* info, const ucontext_t* ucontext) { char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination if (prctl(PR_GET_NAME, reinterpret_cast(thread_name), 0, 0, 0) != 0) { strcpy(thread_name, ""); @@ -186,7 +186,8 @@ static void log_signal_summary(const siginfo_t* info) { // Many signals don't have an address or sender. char addr_desc[32] = ""; // ", fault addr 0x1234" if (signal_has_si_addr(info)) { - async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr); + async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", + reinterpret_cast(get_fault_address(info, ucontext))); } pid_t self_pid = __getpid(); char sender_desc[32] = {}; // " from pid 1234, uid 666" @@ -476,6 +477,8 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c // making a syscall and checking errno. ErrnoRestorer restorer; + auto *ucontext = static_cast(context); + // It's possible somebody cleared the SA_SIGINFO flag, which would mean // our "info" arg holds an undefined value. if (!have_siginfo(signal_number)) { @@ -522,7 +525,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(context), process_info.abort_msg); + debuggerd_fallback_handler(info, ucontext, process_info.abort_msg); resend_signal(info); return; } @@ -534,7 +537,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c return; } - log_signal_summary(info); + log_signal_summary(info, ucontext); debugger_thread_info thread_info = { .crashing_tid = __gettid(), diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h index 4f681c260..35c3fd646 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h @@ -41,4 +41,7 @@ struct ProcessInfo { uintptr_t fdsan_table_address = 0; uintptr_t gwp_asan_state = 0; uintptr_t gwp_asan_metadata = 0; + + bool has_fault_address = false; + uintptr_t fault_address = 0; }; diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h index 75bac87d3..7bfcf5d6e 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h @@ -93,4 +93,6 @@ void get_signal_sender(char* buf, size_t n, const siginfo_t*); const char* get_signame(const siginfo_t*); const char* get_sigcode(const siginfo_t*); +uintptr_t get_fault_address(const siginfo_t* siginfo, const ucontext_t* ucontext); + #endif // _DEBUGGERD_UTILITY_H diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp index b3f059c1c..70b8817be 100644 --- a/debuggerd/libdebuggerd/tombstone.cpp +++ b/debuggerd/libdebuggerd/tombstone.cpp @@ -154,16 +154,16 @@ static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Ma } static void dump_signal_info(log_t* log, const ThreadInfo& thread_info, - unwindstack::Memory* process_memory) { + const ProcessInfo& process_info, unwindstack::Memory* process_memory) { char addr_desc[64]; // ", fault addr 0x1234" - if (signal_has_si_addr(thread_info.siginfo)) { - void* addr = thread_info.siginfo->si_addr; + if (process_info.has_fault_address) { + size_t addr = process_info.fault_address; if (thread_info.siginfo->si_signo == SIGILL) { uint32_t instruction = {}; - process_memory->Read(reinterpret_cast(addr), &instruction, sizeof(instruction)); - snprintf(addr_desc, sizeof(addr_desc), "%p (*pc=%#08x)", addr, instruction); + process_memory->Read(addr, &instruction, sizeof(instruction)); + snprintf(addr_desc, sizeof(addr_desc), "0x%zx (*pc=%#08x)", addr, instruction); } else { - snprintf(addr_desc, sizeof(addr_desc), "%p", addr); + snprintf(addr_desc, sizeof(addr_desc), "0x%zx", addr); } } else { snprintf(addr_desc, sizeof(addr_desc), "--------"); @@ -384,7 +384,7 @@ static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const Threa dump_thread_info(log, thread_info); if (thread_info.siginfo) { - dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get()); + dump_signal_info(log, thread_info, process_info, unwinder->GetProcessMemory().get()); } std::unique_ptr gwp_asan_crash_data; diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp index 0a1d2a4da..3bf28b6c2 100644 --- a/debuggerd/libdebuggerd/utility.cpp +++ b/debuggerd/libdebuggerd/utility.cpp @@ -449,3 +449,40 @@ void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* pref _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str()); } } + +#if defined(__aarch64__) +#define FAR_MAGIC 0x46415201 + +struct far_context { + struct _aarch64_ctx head; + __u64 far; +}; +#endif + +uintptr_t get_fault_address(const siginfo_t* siginfo, const ucontext_t* ucontext) { + (void)ucontext; +#if defined(__aarch64__) + // This relies on a kernel patch: + // https://patchwork.kernel.org/patch/11435077/ + // that hasn't been accepted into the kernel yet. TODO(pcc): Update this to + // use the official interface once it lands. + auto* begin = reinterpret_cast(ucontext->uc_mcontext.__reserved); + auto* end = begin + sizeof(ucontext->uc_mcontext.__reserved); + auto* ptr = begin; + while (1) { + auto* ctx = reinterpret_cast(ptr); + if (ctx->magic == 0) { + break; + } + if (ctx->magic == FAR_MAGIC) { + auto* far_ctx = reinterpret_cast(ctx); + return far_ctx->far; + } + ptr += ctx->size; + if (ctx->size % sizeof(void*) != 0 || ptr < begin || ptr >= end) { + break; + } + } +#endif + return reinterpret_cast(siginfo->si_addr); +}