Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bcc00c5c authored by Christopher Ferris's avatar Christopher Ferris Committed by Gerrit Code Review
Browse files

Merge "Add cause message when stack overflow is detected."

parents 3f2d1763 481e8379
Loading
Loading
Loading
Loading
+27 −0
Original line number Original line Diff line number Diff line
@@ -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)");
}
+40 −3
Original line number Original line Diff line number Diff line
@@ -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)) {
@@ -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,
@@ -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) {