Add cause message when stack overflow is detected.
Test: Ran new unit tests.
Test: Ran crasher stack-overflow, crasher64 stack-overflow and verified
Test: stack overflow cause is shown.
Test: Ran stack overflow app and verified tombstone includes stack-overflow
Test: message.
Change-Id: I9bb01186dff5ed81c77d84b6aaedb5332ddd7256
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index d246722..da2ba58 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -86,7 +86,39 @@
_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;
if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
if (si->si_addr < reinterpret_cast<void*>(4096)) {
@@ -101,11 +133,16 @@
cause = "call to kuser_memory_barrier";
} else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) {
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) {
- 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) {
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) {
cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
@@ -447,7 +484,7 @@
if (thread_info.siginfo) {
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) {