aboutsummaryrefslogtreecommitdiff
path: root/absl/debugging/internal/stacktrace_x86-inl.inc
diff options
context:
space:
mode:
Diffstat (limited to 'absl/debugging/internal/stacktrace_x86-inl.inc')
-rw-r--r--absl/debugging/internal/stacktrace_x86-inl.inc29
1 files changed, 26 insertions, 3 deletions
diff --git a/absl/debugging/internal/stacktrace_x86-inl.inc b/absl/debugging/internal/stacktrace_x86-inl.inc
index 9fbfcf76..1975ba74 100644
--- a/absl/debugging/internal/stacktrace_x86-inl.inc
+++ b/absl/debugging/internal/stacktrace_x86-inl.inc
@@ -29,6 +29,7 @@
#include <cstdint>
#include <limits>
+#include "absl/base/attributes.h"
#include "absl/base/macros.h"
#include "absl/base/port.h"
#include "absl/debugging/internal/address_is_readable.h"
@@ -39,7 +40,7 @@ using absl::debugging_internal::AddressIsReadable;
#if defined(__linux__) && defined(__i386__)
// Count "push %reg" instructions in VDSO __kernel_vsyscall(),
-// preceeding "syscall" or "sysenter".
+// preceding "syscall" or "sysenter".
// If __kernel_vsyscall uses frame pointer, answer 0.
//
// kMaxBytes tells how many instruction bytes of __kernel_vsyscall
@@ -111,6 +112,10 @@ static int CountPushInstructions(const unsigned char *const addr) {
// Assume stack frames larger than 100,000 bytes are bogus.
static const int kMaxFrameBytes = 100000;
+// Stack end to use when we don't know the actual stack end
+// (effectively just the end of address space).
+constexpr uintptr_t kUnknownStackEnd =
+ std::numeric_limits<size_t>::max() - sizeof(void *);
// Returns the stack frame pointer from signal context, 0 if unknown.
// vuc is a ucontext_t *. We use void* to avoid the use
@@ -257,8 +262,26 @@ static void **NextStackFrame(void **old_fp, const void *uc,
// With the stack growing downwards, older stack frame must be
// at a greater address that the current one.
if (new_fp_u <= old_fp_u) return nullptr;
- if (new_fp_u - old_fp_u > kMaxFrameBytes) return nullptr;
+ // If we get a very large frame size, it may be an indication that we
+ // guessed frame pointers incorrectly and now risk a paging fault
+ // dereferencing a wrong frame pointer. Or maybe not because large frames
+ // are possible as well. The main stack is assumed to be readable,
+ // so we assume the large frame is legit if we know the real stack bounds
+ // and are within the stack.
+ if (new_fp_u - old_fp_u > kMaxFrameBytes) {
+ if (stack_high < kUnknownStackEnd &&
+ static_cast<size_t>(getpagesize()) < stack_low) {
+ // Stack bounds are known.
+ if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) {
+ // new_fp_u is not within the known stack.
+ return nullptr;
+ }
+ } else {
+ // Stack bounds are unknown, prefer truncated stack to possible crash.
+ return nullptr;
+ }
+ }
if (stack_low < old_fp_u && old_fp_u <= stack_high) {
// Old BP was in the expected stack region...
if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) {
@@ -311,7 +334,7 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
// Assume that the first page is not stack.
size_t stack_low = static_cast<size_t>(getpagesize());
- size_t stack_high = std::numeric_limits<size_t>::max() - sizeof(void *);
+ size_t stack_high = kUnknownStackEnd;
while (fp && n < max_depth) {
if (*(fp + 1) == reinterpret_cast<void *>(0)) {