Loading debuggerd/debuggerd_test.cpp +27 −0 Original line number Original line Diff line number Diff line Loading @@ -1099,3 +1099,30 @@ TEST(tombstoned, interceptless_backtrace) { // This should be good enough, though... // This should be good enough, though... ASSERT_LT(diff, 10) << "too many new tombstones; is something crashing in the background?"; ASSERT_LT(diff, 10) << "too many new tombstones; is something crashing in the background?"; } } static __attribute__((__noinline__)) void overflow_stack(void* p) { void* buf[1]; buf[0] = p; static volatile void* global = buf; if (global) { global = buf; overflow_stack(&buf); } } TEST_F(CrasherTest, stack_overflow) { int intercept_result; unique_fd output_fd; StartProcess([]() { overflow_stack(nullptr); }); 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); ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)"); } debuggerd/libdebuggerd/tombstone.cpp +40 −3 Original line number Original line Diff line number Diff line Loading @@ -86,7 +86,39 @@ static void dump_timestamp(log_t* log, time_t time) { _LOG(log, logtype::HEADER, "Timestamp: %s\n", buf); _LOG(log, logtype::HEADER, "Timestamp: %s\n", buf); } } static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps) { static std::string get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp, unwindstack::Maps* maps) { static constexpr uint64_t kMaxDifferenceBytes = 256; uint64_t difference; if (sp >= fault_addr) { difference = sp - fault_addr; } else { difference = fault_addr - sp; } if (difference <= kMaxDifferenceBytes) { // The faulting address is close to the current sp, check if the sp // indicates a stack overflow. // On arm, the sp does not get updated when the instruction faults. // In this case, the sp will still be in a valid map, which is the // last case below. // On aarch64, the sp does get updated when the instruction faults. // In this case, the sp will be in either an invalid map if triggered // on the main thread, or in a guard map if in another thread, which // will be the first case or second case from below. unwindstack::MapInfo* map_info = maps->Find(sp); if (map_info == nullptr) { return "stack pointer is in a non-existent map; likely due to stack overflow."; } else if ((map_info->flags & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) { return "stack pointer is not in a rw map; likely due to stack overflow."; } else if ((sp - map_info->start) <= kMaxDifferenceBytes) { return "stack pointer is close to top of stack; likely stack overflow."; } } return ""; } static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps, unwindstack::Regs* regs) { std::string cause; std::string cause; if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) { if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) { if (si->si_addr < reinterpret_cast<void*>(4096)) { if (si->si_addr < reinterpret_cast<void*>(4096)) { Loading @@ -101,11 +133,16 @@ static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Ma cause = "call to kuser_memory_barrier"; cause = "call to kuser_memory_barrier"; } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) { } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) { cause = "call to kuser_cmpxchg64"; cause = "call to kuser_cmpxchg64"; } else { cause = get_stack_overflow_cause(reinterpret_cast<uint64_t>(si->si_addr), regs->sp(), maps); } } } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) { } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) { unwindstack::MapInfo* map_info = maps->Find(reinterpret_cast<uint64_t>(si->si_addr)); uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr); unwindstack::MapInfo* map_info = maps->Find(fault_addr); if (map_info != nullptr && map_info->flags == PROT_EXEC) { if (map_info != nullptr && map_info->flags == PROT_EXEC) { cause = "execute-only (no-read) memory access error; likely due to data in .text."; cause = "execute-only (no-read) memory access error; likely due to data in .text."; } else { cause = get_stack_overflow_cause(fault_addr, regs->sp(), maps); } } } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) { } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) { cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING, cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING, Loading Loading @@ -447,7 +484,7 @@ static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const Threa if (thread_info.siginfo) { if (thread_info.siginfo) { dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get()); dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get()); dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps()); dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get()); } } if (primary_thread) { if (primary_thread) { Loading Loading
debuggerd/debuggerd_test.cpp +27 −0 Original line number Original line Diff line number Diff line Loading @@ -1099,3 +1099,30 @@ TEST(tombstoned, interceptless_backtrace) { // This should be good enough, though... // This should be good enough, though... ASSERT_LT(diff, 10) << "too many new tombstones; is something crashing in the background?"; ASSERT_LT(diff, 10) << "too many new tombstones; is something crashing in the background?"; } } static __attribute__((__noinline__)) void overflow_stack(void* p) { void* buf[1]; buf[0] = p; static volatile void* global = buf; if (global) { global = buf; overflow_stack(&buf); } } TEST_F(CrasherTest, stack_overflow) { int intercept_result; unique_fd output_fd; StartProcess([]() { overflow_stack(nullptr); }); 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); ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)"); }
debuggerd/libdebuggerd/tombstone.cpp +40 −3 Original line number Original line Diff line number Diff line Loading @@ -86,7 +86,39 @@ static void dump_timestamp(log_t* log, time_t time) { _LOG(log, logtype::HEADER, "Timestamp: %s\n", buf); _LOG(log, logtype::HEADER, "Timestamp: %s\n", buf); } } static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps) { static std::string get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp, unwindstack::Maps* maps) { static constexpr uint64_t kMaxDifferenceBytes = 256; uint64_t difference; if (sp >= fault_addr) { difference = sp - fault_addr; } else { difference = fault_addr - sp; } if (difference <= kMaxDifferenceBytes) { // The faulting address is close to the current sp, check if the sp // indicates a stack overflow. // On arm, the sp does not get updated when the instruction faults. // In this case, the sp will still be in a valid map, which is the // last case below. // On aarch64, the sp does get updated when the instruction faults. // In this case, the sp will be in either an invalid map if triggered // on the main thread, or in a guard map if in another thread, which // will be the first case or second case from below. unwindstack::MapInfo* map_info = maps->Find(sp); if (map_info == nullptr) { return "stack pointer is in a non-existent map; likely due to stack overflow."; } else if ((map_info->flags & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) { return "stack pointer is not in a rw map; likely due to stack overflow."; } else if ((sp - map_info->start) <= kMaxDifferenceBytes) { return "stack pointer is close to top of stack; likely stack overflow."; } } return ""; } static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps, unwindstack::Regs* regs) { std::string cause; std::string cause; if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) { if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) { if (si->si_addr < reinterpret_cast<void*>(4096)) { if (si->si_addr < reinterpret_cast<void*>(4096)) { Loading @@ -101,11 +133,16 @@ static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Ma cause = "call to kuser_memory_barrier"; cause = "call to kuser_memory_barrier"; } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) { } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) { cause = "call to kuser_cmpxchg64"; cause = "call to kuser_cmpxchg64"; } else { cause = get_stack_overflow_cause(reinterpret_cast<uint64_t>(si->si_addr), regs->sp(), maps); } } } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) { } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) { unwindstack::MapInfo* map_info = maps->Find(reinterpret_cast<uint64_t>(si->si_addr)); uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr); unwindstack::MapInfo* map_info = maps->Find(fault_addr); if (map_info != nullptr && map_info->flags == PROT_EXEC) { if (map_info != nullptr && map_info->flags == PROT_EXEC) { cause = "execute-only (no-read) memory access error; likely due to data in .text."; cause = "execute-only (no-read) memory access error; likely due to data in .text."; } else { cause = get_stack_overflow_cause(fault_addr, regs->sp(), maps); } } } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) { } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) { cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING, cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING, Loading Loading @@ -447,7 +484,7 @@ static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const Threa if (thread_info.siginfo) { if (thread_info.siginfo) { dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get()); dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get()); dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps()); dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get()); } } if (primary_thread) { if (primary_thread) { Loading