aboutsummaryrefslogtreecommitdiff
path: root/absl/debugging
diff options
context:
space:
mode:
Diffstat (limited to 'absl/debugging')
-rw-r--r--absl/debugging/BUILD.bazel17
-rw-r--r--absl/debugging/CMakeLists.txt16
-rw-r--r--absl/debugging/failure_signal_handler.cc21
-rw-r--r--absl/debugging/failure_signal_handler.h2
-rw-r--r--absl/debugging/internal/demangle_test.cc2
-rw-r--r--absl/debugging/internal/stack_consumption.cc2
-rw-r--r--absl/debugging/internal/stacktrace_aarch64-inl.inc43
-rw-r--r--absl/debugging/internal/stacktrace_powerpc-inl.inc2
-rw-r--r--absl/debugging/internal/stacktrace_x86-inl.inc29
-rw-r--r--absl/debugging/internal/symbolize.h2
-rw-r--r--absl/debugging/leak_check.cc4
-rw-r--r--absl/debugging/leak_check.h2
-rw-r--r--absl/debugging/stacktrace_test.cc47
-rw-r--r--absl/debugging/symbolize_elf.inc10
14 files changed, 167 insertions, 32 deletions
diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel
index a40285c8..e89dbae8 100644
--- a/absl/debugging/BUILD.bazel
+++ b/absl/debugging/BUILD.bazel
@@ -49,10 +49,23 @@ cc_library(
":debugging_internal",
"//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:dynamic_annotations",
"//absl/base:raw_logging_internal",
],
)
+cc_test(
+ name = "stacktrace_test",
+ srcs = ["stacktrace_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":stacktrace",
+ "//absl/base:core_headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
cc_library(
name = "symbolize",
srcs = [
@@ -71,6 +84,10 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS + select({
"//absl:msvc_compiler": ["-DEFAULTLIB:dbghelp.lib"],
"//absl:clang-cl_compiler": ["-DEFAULTLIB:dbghelp.lib"],
+ "//absl:mingw_compiler": [
+ "-DEFAULTLIB:dbghelp.lib",
+ "-ldbghelp",
+ ],
"//conditions:default": [],
}),
deps = [
diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt
index 051e7017..ef6b4965 100644
--- a/absl/debugging/CMakeLists.txt
+++ b/absl/debugging/CMakeLists.txt
@@ -41,10 +41,24 @@ absl_cc_library(
absl::debugging_internal
absl::config
absl::core_headers
+ absl::dynamic_annotations
absl::raw_logging_internal
PUBLIC
)
+absl_cc_test(
+ NAME
+ stacktrace_test
+ SRCS
+ "stacktrace_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::stacktrace
+ absl::core_headers
+ GTest::gmock_main
+)
+
absl_cc_library(
NAME
symbolize
@@ -62,7 +76,7 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
- $<$<BOOL:${MINGW}>:"dbghelp">
+ $<$<BOOL:${MINGW}>:-ldbghelp>
DEPS
absl::debugging_internal
absl::demangle_internal
diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc
index 5e8f0b05..9f399d02 100644
--- a/absl/debugging/failure_signal_handler.cc
+++ b/absl/debugging/failure_signal_handler.cc
@@ -33,6 +33,10 @@
#include <sys/mman.h>
#endif
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
+
#include <algorithm>
#include <atomic>
#include <cerrno>
@@ -138,7 +142,8 @@ static bool SetupAlternateStackOnce() {
const size_t page_mask = static_cast<size_t>(sysconf(_SC_PAGESIZE)) - 1;
#endif
size_t stack_size =
- (std::max<size_t>(SIGSTKSZ, 65536) + page_mask) & ~page_mask;
+ (std::max(static_cast<size_t>(SIGSTKSZ), size_t{65536}) + page_mask) &
+ ~page_mask;
#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER)
// Account for sanitizer instrumentation requiring additional stack space.
@@ -171,6 +176,20 @@ static bool SetupAlternateStackOnce() {
if (sigaltstack(&sigstk, nullptr) != 0) {
ABSL_RAW_LOG(FATAL, "sigaltstack() failed with errno=%d", errno);
}
+
+#ifdef __linux__
+#if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME)
+ // Make a best-effort attempt to name the allocated region in
+ // /proc/$PID/smaps.
+ //
+ // The call to prctl() may fail if the kernel was not configured with the
+ // CONFIG_ANON_VMA_NAME kernel option. This is OK since the call is
+ // primarily a debugging aid.
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, sigstk.ss_sp, sigstk.ss_size,
+ "absl-signalstack");
+#endif
+#endif // __linux__
+
return true;
}
diff --git a/absl/debugging/failure_signal_handler.h b/absl/debugging/failure_signal_handler.h
index 500115c0..5e034785 100644
--- a/absl/debugging/failure_signal_handler.h
+++ b/absl/debugging/failure_signal_handler.h
@@ -62,7 +62,7 @@ struct FailureSignalHandlerOptions {
// If true, try to run signal handlers on an alternate stack (if supported on
// the given platform). An alternate stack is useful for program crashes due
// to a stack overflow; by running on a alternate stack, the signal handler
- // may run even when normal stack space has been exausted. The downside of
+ // may run even when normal stack space has been exhausted. The downside of
// using an alternate stack is that extra memory for the alternate stack needs
// to be pre-allocated.
bool use_alternate_stack = true;
diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc
index 8463a2b7..2fa4143a 100644
--- a/absl/debugging/internal/demangle_test.cc
+++ b/absl/debugging/internal/demangle_test.cc
@@ -38,7 +38,7 @@ static const char *DemangleIt(const char * const mangled) {
}
}
-// Test corner cases of bounary conditions.
+// Test corner cases of boundary conditions.
TEST(Demangle, CornerCases) {
char tmp[10];
EXPECT_TRUE(Demangle("_Z6foobarv", tmp, sizeof(tmp)));
diff --git a/absl/debugging/internal/stack_consumption.cc b/absl/debugging/internal/stack_consumption.cc
index 51348649..6d974992 100644
--- a/absl/debugging/internal/stack_consumption.cc
+++ b/absl/debugging/internal/stack_consumption.cc
@@ -162,7 +162,7 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) {
// versions of musl have a bug that rejects ss_size==0. Work around this by
// setting ss_size to MINSIGSTKSZ, which should be ignored by the kernel
// when SS_DISABLE is set.
- old_sigstk.ss_size = MINSIGSTKSZ;
+ old_sigstk.ss_size = static_cast<size_t>(MINSIGSTKSZ);
}
ABSL_RAW_CHECK(sigaltstack(&old_sigstk, nullptr) == 0,
"sigaltstack() failed");
diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc
index 891942c0..b66beba2 100644
--- a/absl/debugging/internal/stacktrace_aarch64-inl.inc
+++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc
@@ -19,7 +19,7 @@
#include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems
#include "absl/debugging/stacktrace.h"
-static const uintptr_t kUnknownFrameSize = 0;
+static const size_t kUnknownFrameSize = 0;
#if defined(__linux__)
// Returns the address of the VDSO __kernel_rt_sigreturn function, if present.
@@ -65,11 +65,12 @@ static const unsigned char* GetKernelRtSigreturnAddress() {
// Compute the size of a stack frame in [low..high). We assume that
// low < high. Return size of kUnknownFrameSize.
template<typename T>
-static inline uintptr_t ComputeStackFrameSize(const T* low,
- const T* high) {
+static inline size_t ComputeStackFrameSize(const T* low,
+ const T* high) {
const char* low_char_ptr = reinterpret_cast<const char *>(low);
const char* high_char_ptr = reinterpret_cast<const char *>(high);
- return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize;
+ return low < high ? static_cast<size_t>(high_char_ptr - low_char_ptr)
+ : kUnknownFrameSize;
}
// Given a pointer to a stack frame, locate and return the calling
@@ -117,8 +118,8 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc) {
// Check frame size. In strict mode, we assume frames to be under
// 100,000 bytes. In non-strict mode, we relax the limit to 1MB.
if (check_frame_size) {
- const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
- const uintptr_t frame_size =
+ const size_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
+ const size_t frame_size =
ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
if (frame_size == kUnknownFrameSize || frame_size > max_size)
return nullptr;
@@ -137,41 +138,45 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
#else
# error reading stack point not yet supported on this platform.
#endif
-
skip_count++; // Skip the frame for this function.
int n = 0;
// The frame pointer points to low address of a frame. The first 64-bit
// word of a frame points to the next frame up the call chain, which normally
// is just after the high address of the current frame. The second word of
- // a frame contains return adress of to the caller. To find a pc value
+ // a frame contains return address of to the caller. To find a pc value
// associated with the current frame, we need to go down a level in the call
// chain. So we remember return the address of the last frame seen. This
// does not work for the first stack frame, which belongs to UnwindImp() but
// we skip the frame for UnwindImp() anyway.
void* prev_return_address = nullptr;
+ // The nth frame size is the difference between the nth frame pointer and the
+ // the frame pointer below it in the call chain. There is no frame below the
+ // leaf frame, but this function is the leaf anyway, and we skip it.
+ void** prev_frame_pointer = nullptr;
- while (frame_pointer && n < max_depth) {
- // The absl::GetStackFrames routine is called when we are in some
- // informational context (the failure signal handler for example).
- // Use the non-strict unwinding rules to produce a stack trace
- // that is as complete as possible (even if it contains a few bogus
- // entries in some rare cases).
- void **next_frame_pointer =
- NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
-
+ while (frame_pointer && n < max_depth) {
if (skip_count > 0) {
skip_count--;
} else {
result[n] = prev_return_address;
if (IS_STACK_FRAMES) {
- sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer);
+ sizes[n] = static_cast<int>(
+ ComputeStackFrameSize(prev_frame_pointer, frame_pointer));
}
n++;
}
prev_return_address = frame_pointer[1];
- frame_pointer = next_frame_pointer;
+ prev_frame_pointer = frame_pointer;
+ // The absl::GetStackFrames routine is called when we are in some
+ // informational context (the failure signal handler for example).
+ // Use the non-strict unwinding rules to produce a stack trace
+ // that is as complete as possible (even if it contains a few bogus
+ // entries in some rare cases).
+ frame_pointer =
+ NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
}
+
if (min_dropped_frames != nullptr) {
// Implementation detail: we clamp the max of frames we are willing to
// count, so as not to spend too much time in the loop below.
diff --git a/absl/debugging/internal/stacktrace_powerpc-inl.inc b/absl/debugging/internal/stacktrace_powerpc-inl.inc
index 085cef67..a49ed2f7 100644
--- a/absl/debugging/internal/stacktrace_powerpc-inl.inc
+++ b/absl/debugging/internal/stacktrace_powerpc-inl.inc
@@ -57,7 +57,7 @@ static inline void *StacktracePowerPCGetLR(void **sp) {
// This check is in case the compiler doesn't define _CALL_SYSV.
return *(sp+1);
#else
-#error Need to specify the PPC ABI for your archiecture.
+#error Need to specify the PPC ABI for your architecture.
#endif
}
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)) {
diff --git a/absl/debugging/internal/symbolize.h b/absl/debugging/internal/symbolize.h
index 27d5e652..5593fde6 100644
--- a/absl/debugging/internal/symbolize.h
+++ b/absl/debugging/internal/symbolize.h
@@ -115,7 +115,7 @@ bool RemoveSymbolDecorator(int ticket);
// Remove all installed decorators. Returns true if successful, false if
// symbolization is currently in progress.
-bool RemoveAllSymbolDecorators(void);
+bool RemoveAllSymbolDecorators();
// Registers an address range to a file mapping.
//
diff --git a/absl/debugging/leak_check.cc b/absl/debugging/leak_check.cc
index 195e82bf..fdb8798b 100644
--- a/absl/debugging/leak_check.cc
+++ b/absl/debugging/leak_check.cc
@@ -65,8 +65,8 @@ bool LeakCheckerIsActive() { return false; }
void DoIgnoreLeak(const void*) { }
void RegisterLivePointers(const void*, size_t) { }
void UnRegisterLivePointers(const void*, size_t) { }
-LeakCheckDisabler::LeakCheckDisabler() { }
-LeakCheckDisabler::~LeakCheckDisabler() { }
+LeakCheckDisabler::LeakCheckDisabler() = default;
+LeakCheckDisabler::~LeakCheckDisabler() = default;
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/debugging/leak_check.h b/absl/debugging/leak_check.h
index eff162f6..6bd79406 100644
--- a/absl/debugging/leak_check.h
+++ b/absl/debugging/leak_check.h
@@ -37,7 +37,7 @@
// not also use AddressSanitizer). To use the mode, simply pass
// `-fsanitize=leak` to both the compiler and linker. Since GCC does not
// currently provide a way of detecting this mode at compile-time, GCC users
-// must also pass -DLEAK_SANIITIZER to the compiler. An example Bazel command
+// must also pass -DLEAK_SANITIZER to the compiler. An example Bazel command
// could be
//
// $ bazel test --copt=-DLEAK_SANITIZER --copt=-fsanitize=leak
diff --git a/absl/debugging/stacktrace_test.cc b/absl/debugging/stacktrace_test.cc
new file mode 100644
index 00000000..78ce7ad0
--- /dev/null
+++ b/absl/debugging/stacktrace_test.cc
@@ -0,0 +1,47 @@
+// Copyright 2023 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/debugging/stacktrace.h"
+
+#include "gtest/gtest.h"
+#include "absl/base/macros.h"
+#include "absl/base/optimization.h"
+
+namespace {
+
+// This test is currently only known to pass on linux/x86_64.
+#if defined(__linux__) && defined(__x86_64__)
+ABSL_ATTRIBUTE_NOINLINE void Unwind(void* p) {
+ ABSL_ATTRIBUTE_UNUSED static void* volatile sink = p;
+ constexpr int kSize = 16;
+ void* stack[kSize];
+ int frames[kSize];
+ absl::GetStackTrace(stack, kSize, 0);
+ absl::GetStackFrames(stack, frames, kSize, 0);
+}
+
+ABSL_ATTRIBUTE_NOINLINE void HugeFrame() {
+ char buffer[1 << 20];
+ Unwind(buffer);
+ ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
+}
+
+TEST(StackTrace, HugeFrame) {
+ // Ensure that the unwinder is not confused by very large stack frames.
+ HugeFrame();
+ ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
+}
+#endif
+
+} // namespace
diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc
index ffb4eecf..0fee89f2 100644
--- a/absl/debugging/symbolize_elf.inc
+++ b/absl/debugging/symbolize_elf.inc
@@ -532,6 +532,11 @@ bool ForEachSection(int fd,
return false;
}
+ // Technically it can be larger, but in practice this never happens.
+ if (elf_header.e_shentsize != sizeof(ElfW(Shdr))) {
+ return false;
+ }
+
ElfW(Shdr) shstrtab;
off_t shstrtab_offset = static_cast<off_t>(elf_header.e_shoff) +
elf_header.e_shentsize * elf_header.e_shstrndx;
@@ -584,6 +589,11 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len,
return false;
}
+ // Technically it can be larger, but in practice this never happens.
+ if (elf_header.e_shentsize != sizeof(ElfW(Shdr))) {
+ return false;
+ }
+
ElfW(Shdr) shstrtab;
off_t shstrtab_offset = static_cast<off_t>(elf_header.e_shoff) +
elf_header.e_shentsize * elf_header.e_shstrndx;