diff options
Diffstat (limited to 'absl/debugging/internal')
-rw-r--r-- | absl/debugging/internal/address_is_readable.cc | 2 | ||||
-rw-r--r-- | absl/debugging/internal/demangle.cc | 105 | ||||
-rw-r--r-- | absl/debugging/internal/demangle.h | 2 | ||||
-rw-r--r-- | absl/debugging/internal/demangle_test.cc | 24 | ||||
-rw-r--r-- | absl/debugging/internal/elf_mem_image.cc | 25 | ||||
-rw-r--r-- | absl/debugging/internal/examine_stack.cc | 14 | ||||
-rw-r--r-- | absl/debugging/internal/stacktrace_aarch64-inl.inc | 20 | ||||
-rw-r--r-- | absl/debugging/internal/stacktrace_generic-inl.inc | 2 | ||||
-rw-r--r-- | absl/debugging/internal/stacktrace_riscv-inl.inc | 125 | ||||
-rw-r--r-- | absl/debugging/internal/stacktrace_win32-inl.inc | 9 | ||||
-rw-r--r-- | absl/debugging/internal/stacktrace_x86-inl.inc | 41 | ||||
-rw-r--r-- | absl/debugging/internal/vdso_support.cc | 5 |
12 files changed, 206 insertions, 168 deletions
diff --git a/absl/debugging/internal/address_is_readable.cc b/absl/debugging/internal/address_is_readable.cc index 4be6256b..91eaa76f 100644 --- a/absl/debugging/internal/address_is_readable.cc +++ b/absl/debugging/internal/address_is_readable.cc @@ -52,7 +52,7 @@ namespace debugging_internal { bool AddressIsReadable(const void *addr) { // Align address on 8-byte boundary. On aarch64, checking last // byte before inaccessible page returned unexpected EFAULT. - const uintptr_t u_addr = reinterpret_cast<uintptr_t>(addr) & ~7; + const uintptr_t u_addr = reinterpret_cast<uintptr_t>(addr) & ~uintptr_t{7}; addr = reinterpret_cast<const void *>(u_addr); // rt_sigprocmask below will succeed for this input. diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc index 93ae3279..f2832915 100644 --- a/absl/debugging/internal/demangle.cc +++ b/absl/debugging/internal/demangle.cc @@ -151,12 +151,12 @@ static const AbbrevPair kSubstitutionList[] = { // State needed for demangling. This struct is copied in almost every stack // frame, so every byte counts. typedef struct { - int mangled_idx; // Cursor of mangled name. - int out_cur_idx; // Cursor of output string. - int prev_name_idx; // For constructors/destructors. - signed int prev_name_length : 16; // For constructors/destructors. - signed int nest_level : 15; // For nested names. - unsigned int append : 1; // Append flag. + int mangled_idx; // Cursor of mangled name. + int out_cur_idx; // Cursor of output string. + int prev_name_idx; // For constructors/destructors. + unsigned int prev_name_length : 16; // For constructors/destructors. + signed int nest_level : 15; // For nested names. + unsigned int append : 1; // Append flag. // Note: for some reason MSVC can't pack "bool append : 1" into the same int // with the above two fields, so we use an int instead. Amusingly it can pack // "signed bool" as expected, but relying on that to continue to be a legal @@ -235,8 +235,8 @@ static size_t StrLen(const char *str) { } // Returns true if "str" has at least "n" characters remaining. -static bool AtLeastNumCharsRemaining(const char *str, int n) { - for (int i = 0; i < n; ++i) { +static bool AtLeastNumCharsRemaining(const char *str, size_t n) { + for (size_t i = 0; i < n; ++i) { if (str[i] == '\0') { return false; } @@ -253,18 +253,20 @@ static bool StrPrefix(const char *str, const char *prefix) { return prefix[i] == '\0'; // Consumed everything in "prefix". } -static void InitState(State *state, const char *mangled, char *out, - int out_size) { +static void InitState(State* state, + const char* mangled, + char* out, + size_t out_size) { state->mangled_begin = mangled; state->out = out; - state->out_end_idx = out_size; + state->out_end_idx = static_cast<int>(out_size); state->recursion_depth = 0; state->steps = 0; state->parse_state.mangled_idx = 0; state->parse_state.out_cur_idx = 0; state->parse_state.prev_name_idx = 0; - state->parse_state.prev_name_length = -1; + state->parse_state.prev_name_length = 0; state->parse_state.nest_level = -1; state->parse_state.append = true; } @@ -356,8 +358,8 @@ static bool ZeroOrMore(ParseFunc parse_func, State *state) { // Append "str" at "out_cur_idx". If there is an overflow, out_cur_idx is // set to out_end_idx+1. The output string is ensured to // always terminate with '\0' as long as there is no overflow. -static void Append(State *state, const char *const str, const int length) { - for (int i = 0; i < length; ++i) { +static void Append(State *state, const char *const str, const size_t length) { + for (size_t i = 0; i < length; ++i) { if (state->parse_state.out_cur_idx + 1 < state->out_end_idx) { // +1 for '\0' state->out[state->parse_state.out_cur_idx++] = str[i]; @@ -420,7 +422,7 @@ static bool EndsWith(State *state, const char chr) { // Append "str" with some tweaks, iff "append" state is true. static void MaybeAppendWithLength(State *state, const char *const str, - const int length) { + const size_t length) { if (state->parse_state.append && length > 0) { // Append a space if the output buffer ends with '<' and "str" // starts with '<' to avoid <<<. @@ -432,14 +434,14 @@ static void MaybeAppendWithLength(State *state, const char *const str, if (state->parse_state.out_cur_idx < state->out_end_idx && (IsAlpha(str[0]) || str[0] == '_')) { state->parse_state.prev_name_idx = state->parse_state.out_cur_idx; - state->parse_state.prev_name_length = length; + state->parse_state.prev_name_length = static_cast<unsigned int>(length); } Append(state, str, length); } } // Appends a positive decimal number to the output if appending is enabled. -static bool MaybeAppendDecimal(State *state, unsigned int val) { +static bool MaybeAppendDecimal(State *state, int val) { // Max {32-64}-bit unsigned int is 20 digits. constexpr size_t kMaxLength = 20; char buf[kMaxLength]; @@ -451,12 +453,12 @@ static bool MaybeAppendDecimal(State *state, unsigned int val) { // one-past-the-end and manipulate one character before the pointer. char *p = &buf[kMaxLength]; do { // val=0 is the only input that should write a leading zero digit. - *--p = (val % 10) + '0'; + *--p = static_cast<char>((val % 10) + '0'); val /= 10; } while (p > buf && val != 0); // 'p' landed on the last character we set. How convenient. - Append(state, p, kMaxLength - (p - buf)); + Append(state, p, kMaxLength - static_cast<size_t>(p - buf)); } return true; @@ -466,7 +468,7 @@ static bool MaybeAppendDecimal(State *state, unsigned int val) { // Returns true so that it can be placed in "if" conditions. static bool MaybeAppend(State *state, const char *const str) { if (state->parse_state.append) { - int length = StrLen(str); + size_t length = StrLen(str); MaybeAppendWithLength(state, str, length); } return true; @@ -521,10 +523,10 @@ static void MaybeCancelLastSeparator(State *state) { // Returns true if the identifier of the given length pointed to by // "mangled_cur" is anonymous namespace. -static bool IdentifierIsAnonymousNamespace(State *state, int length) { +static bool IdentifierIsAnonymousNamespace(State *state, size_t length) { // Returns true if "anon_prefix" is a proper prefix of "mangled_cur". static const char anon_prefix[] = "_GLOBAL__N_"; - return (length > static_cast<int>(sizeof(anon_prefix) - 1) && + return (length > (sizeof(anon_prefix) - 1) && StrPrefix(RemainingInput(state), anon_prefix)); } @@ -542,12 +544,13 @@ static bool ParseUnnamedTypeName(State *state); static bool ParseNumber(State *state, int *number_out); static bool ParseFloatNumber(State *state); static bool ParseSeqId(State *state); -static bool ParseIdentifier(State *state, int length); +static bool ParseIdentifier(State *state, size_t length); static bool ParseOperatorName(State *state, int *arity); static bool ParseSpecialName(State *state); static bool ParseCallOffset(State *state); static bool ParseNVOffset(State *state); static bool ParseVOffset(State *state); +static bool ParseAbiTags(State *state); static bool ParseCtorDtorName(State *state); static bool ParseDecltype(State *state); static bool ParseType(State *state); @@ -601,7 +604,7 @@ static bool ParseSubstitution(State *state, bool accept_std); // // Reference: // - Itanium C++ ABI -// <https://mentorembedded.github.io/cxx-abi/abi.html#mangling> +// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling> // <mangled-name> ::= _Z <encoding> static bool ParseMangledName(State *state) { @@ -741,17 +744,42 @@ static bool ParsePrefix(State *state) { return true; } -// <unqualified-name> ::= <operator-name> -// ::= <ctor-dtor-name> -// ::= <source-name> -// ::= <local-source-name> // GCC extension; see below. -// ::= <unnamed-type-name> +// <unqualified-name> ::= <operator-name> [<abi-tags>] +// ::= <ctor-dtor-name> [<abi-tags>] +// ::= <source-name> [<abi-tags>] +// ::= <local-source-name> [<abi-tags>] +// ::= <unnamed-type-name> [<abi-tags>] +// +// <local-source-name> is a GCC extension; see below. static bool ParseUnqualifiedName(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; - return (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) || - ParseSourceName(state) || ParseLocalSourceName(state) || - ParseUnnamedTypeName(state)); + if (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) || + ParseSourceName(state) || ParseLocalSourceName(state) || + ParseUnnamedTypeName(state)) { + return ParseAbiTags(state); + } + return false; +} + +// <abi-tags> ::= <abi-tag> [<abi-tags>] +// <abi-tag> ::= B <source-name> +static bool ParseAbiTags(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + while (ParseOneCharToken(state, 'B')) { + ParseState copy = state->parse_state; + MaybeAppend(state, "[abi:"); + + if (!ParseSourceName(state)) { + state->parse_state = copy; + return false; + } + MaybeAppend(state, "]"); + } + + return true; } // <source-name> ::= <positive length number> <identifier> @@ -760,7 +788,8 @@ static bool ParseSourceName(State *state) { if (guard.IsTooComplex()) return false; ParseState copy = state->parse_state; int length = -1; - if (ParseNumber(state, &length) && ParseIdentifier(state, length)) { + if (ParseNumber(state, &length) && + ParseIdentifier(state, static_cast<size_t>(length))) { return true; } state->parse_state = copy; @@ -838,7 +867,7 @@ static bool ParseNumber(State *state, int *number_out) { uint64_t number = 0; for (; *p != '\0'; ++p) { if (IsDigit(*p)) { - number = number * 10 + (*p - '0'); + number = number * 10 + static_cast<uint64_t>(*p - '0'); } else { break; } @@ -853,7 +882,7 @@ static bool ParseNumber(State *state, int *number_out) { state->parse_state.mangled_idx += p - RemainingInput(state); if (number_out != nullptr) { // Note: possibly truncate "number". - *number_out = number; + *number_out = static_cast<int>(number); } return true; } @@ -897,10 +926,10 @@ static bool ParseSeqId(State *state) { } // <identifier> ::= <unqualified source code identifier> (of given length) -static bool ParseIdentifier(State *state, int length) { +static bool ParseIdentifier(State *state, size_t length) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; - if (length < 0 || !AtLeastNumCharsRemaining(RemainingInput(state), length)) { + if (!AtLeastNumCharsRemaining(RemainingInput(state), length)) { return false; } if (IdentifierIsAnonymousNamespace(state, length)) { @@ -1947,7 +1976,7 @@ static bool Overflowed(const State *state) { } // The demangler entry point. -bool Demangle(const char *mangled, char *out, int out_size) { +bool Demangle(const char* mangled, char* out, size_t out_size) { State state; InitState(&state, mangled, out, out_size); return ParseTopLevelMangledName(&state) && !Overflowed(&state) && diff --git a/absl/debugging/internal/demangle.h b/absl/debugging/internal/demangle.h index c314d9bc..e1f15698 100644 --- a/absl/debugging/internal/demangle.h +++ b/absl/debugging/internal/demangle.h @@ -62,7 +62,7 @@ namespace debugging_internal { // Demangle `mangled`. On success, return true and write the // demangled symbol name to `out`. Otherwise, return false. // `out` is modified even if demangling is unsuccessful. -bool Demangle(const char *mangled, char *out, int out_size); +bool Demangle(const char* mangled, char* out, size_t out_size); } // namespace debugging_internal ABSL_NAMESPACE_END diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc index 6b142902..8463a2b7 100644 --- a/absl/debugging/internal/demangle_test.cc +++ b/absl/debugging/internal/demangle_test.cc @@ -102,6 +102,30 @@ TEST(Demangle, Clones) { EXPECT_FALSE(Demangle("_ZL3Foov.isra.2.constprop.", tmp, sizeof(tmp))); } +// Test the GNU abi_tag extension. +TEST(Demangle, AbiTags) { + char tmp[80]; + + // Mangled name generated via: + // struct [[gnu::abi_tag("abc")]] A{}; + // A a; + EXPECT_TRUE(Demangle("_Z1aB3abc", tmp, sizeof(tmp))); + EXPECT_STREQ("a[abi:abc]", tmp); + + // Mangled name generated via: + // struct B { + // B [[gnu::abi_tag("xyz")]] (){}; + // }; + // B b; + EXPECT_TRUE(Demangle("_ZN1BC2B3xyzEv", tmp, sizeof(tmp))); + EXPECT_STREQ("B::B[abi:xyz]()", tmp); + + // Mangled name generated via: + // [[gnu::abi_tag("foo", "bar")]] void C() {} + EXPECT_TRUE(Demangle("_Z1CB3barB3foov", tmp, sizeof(tmp))); + EXPECT_STREQ("C[abi:bar][abi:foo]()", tmp); +} + // Tests that verify that Demangle footprint is within some limit. // They are not to be run under sanitizers as the sanitizers increase // stack consumption by about 4x. diff --git a/absl/debugging/internal/elf_mem_image.cc b/absl/debugging/internal/elf_mem_image.cc index a9d66714..42dcd3cd 100644 --- a/absl/debugging/internal/elf_mem_image.cc +++ b/absl/debugging/internal/elf_mem_image.cc @@ -91,7 +91,7 @@ int ElfMemImage::GetNumSymbols() const { return 0; } // See http://www.caldera.com/developers/gabi/latest/ch5.dynamic.html#hash - return hash_[1]; + return static_cast<int>(hash_[1]); } const ElfW(Sym) *ElfMemImage::GetDynsym(int index) const { @@ -105,11 +105,9 @@ const ElfW(Versym) *ElfMemImage::GetVersym(int index) const { } const ElfW(Phdr) *ElfMemImage::GetPhdr(int index) const { - ABSL_RAW_CHECK(index < ehdr_->e_phnum, "index out of range"); - return GetTableElement<ElfW(Phdr)>(ehdr_, - ehdr_->e_phoff, - ehdr_->e_phentsize, - index); + ABSL_RAW_CHECK(index >= 0 && index < ehdr_->e_phnum, "index out of range"); + return GetTableElement<ElfW(Phdr)>(ehdr_, ehdr_->e_phoff, ehdr_->e_phentsize, + static_cast<size_t>(index)); } const char *ElfMemImage::GetDynstr(ElfW(Word) offset) const { @@ -159,7 +157,8 @@ void ElfMemImage::Init(const void *base) { hash_ = nullptr; strsize_ = 0; verdefnum_ = 0; - link_base_ = ~0L; // Sentinel: PT_LOAD .p_vaddr can't possibly be this. + // Sentinel: PT_LOAD .p_vaddr can't possibly be this. + link_base_ = ~ElfW(Addr){0}; // NOLINT(readability/braces) if (!base) { return; } @@ -218,11 +217,11 @@ void ElfMemImage::Init(const void *base) { } ptrdiff_t relocation = base_as_char - reinterpret_cast<const char *>(link_base_); - ElfW(Dyn) *dynamic_entry = - reinterpret_cast<ElfW(Dyn) *>(dynamic_program_header->p_vaddr + - relocation); + ElfW(Dyn)* dynamic_entry = reinterpret_cast<ElfW(Dyn)*>( + static_cast<intptr_t>(dynamic_program_header->p_vaddr) + relocation); for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) { - const auto value = dynamic_entry->d_un.d_val + relocation; + const auto value = + static_cast<intptr_t>(dynamic_entry->d_un.d_val) + relocation; switch (dynamic_entry->d_tag) { case DT_HASH: hash_ = reinterpret_cast<ElfW(Word) *>(value); @@ -240,10 +239,10 @@ void ElfMemImage::Init(const void *base) { verdef_ = reinterpret_cast<ElfW(Verdef) *>(value); break; case DT_VERDEFNUM: - verdefnum_ = dynamic_entry->d_un.d_val; + verdefnum_ = static_cast<size_t>(dynamic_entry->d_un.d_val); break; case DT_STRSZ: - strsize_ = dynamic_entry->d_un.d_val; + strsize_ = static_cast<size_t>(dynamic_entry->d_un.d_val); break; default: // Unrecognized entries explicitly ignored. diff --git a/absl/debugging/internal/examine_stack.cc b/absl/debugging/internal/examine_stack.cc index 5bdd341e..57863228 100644 --- a/absl/debugging/internal/examine_stack.cc +++ b/absl/debugging/internal/examine_stack.cc @@ -278,13 +278,14 @@ void DumpStackTrace(int min_dropped_frames, int max_num_frames, void* stack_buf[kDefaultDumpStackFramesLimit]; void** stack = stack_buf; int num_stack = kDefaultDumpStackFramesLimit; - int allocated_bytes = 0; + size_t allocated_bytes = 0; if (num_stack >= max_num_frames) { // User requested fewer frames than we already have space for. num_stack = max_num_frames; } else { - const size_t needed_bytes = max_num_frames * sizeof(stack[0]); + const size_t needed_bytes = + static_cast<size_t>(max_num_frames) * sizeof(stack[0]); void* p = Allocate(needed_bytes); if (p != nullptr) { // We got the space. num_stack = max_num_frames; @@ -293,12 +294,13 @@ void DumpStackTrace(int min_dropped_frames, int max_num_frames, } } - size_t depth = absl::GetStackTrace(stack, num_stack, min_dropped_frames + 1); - for (size_t i = 0; i < depth; i++) { + int depth = absl::GetStackTrace(stack, num_stack, min_dropped_frames + 1); + for (int i = 0; i < depth; i++) { if (symbolize_stacktrace) { - DumpPCAndSymbol(writer, writer_arg, stack[i], " "); + DumpPCAndSymbol(writer, writer_arg, stack[static_cast<size_t>(i)], + " "); } else { - DumpPC(writer, writer_arg, stack[i], " "); + DumpPC(writer, writer_arg, stack[static_cast<size_t>(i)], " "); } } diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc index 4f9db9d6..71cdaf09 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 @@ -110,15 +111,15 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc) { } #endif - // aarch64 ABI requires stack pointer to be 16-byte-aligned. - if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 15) != 0) + // The frame pointer should be 8-byte aligned. + if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 7) != 0) return nullptr; // 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; @@ -165,7 +166,8 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int 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(frame_pointer, next_frame_pointer)); } n++; } diff --git a/absl/debugging/internal/stacktrace_generic-inl.inc b/absl/debugging/internal/stacktrace_generic-inl.inc index b2792a1f..5fa169a7 100644 --- a/absl/debugging/internal/stacktrace_generic-inl.inc +++ b/absl/debugging/internal/stacktrace_generic-inl.inc @@ -80,7 +80,7 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, if (IS_STACK_FRAMES) { // No implementation for finding out the stack frame sizes yet. - memset(sizes, 0, sizeof(*sizes) * result_count); + memset(sizes, 0, sizeof(*sizes) * static_cast<size_t>(result_count)); } if (min_dropped_frames != nullptr) { if (size - skip_count - max_depth > 0) { diff --git a/absl/debugging/internal/stacktrace_riscv-inl.inc b/absl/debugging/internal/stacktrace_riscv-inl.inc index 7123b71b..20183fa3 100644 --- a/absl/debugging/internal/stacktrace_riscv-inl.inc +++ b/absl/debugging/internal/stacktrace_riscv-inl.inc @@ -30,56 +30,14 @@ #include <cassert> #include <cstdint> #include <iostream> +#include <limits> +#include <utility> #include "absl/base/attributes.h" -#include "absl/debugging/internal/address_is_readable.h" -#include "absl/debugging/internal/vdso_support.h" #include "absl/debugging/stacktrace.h" static const uintptr_t kUnknownFrameSize = 0; -#if defined(__linux__) -// Returns the address of the VDSO __kernel_rt_sigreturn function, if present. -static const unsigned char *GetKernelRtSigreturnAddress() { - constexpr uintptr_t kImpossibleAddress = 0; - ABSL_CONST_INIT static std::atomic<uintptr_t> memoized(kImpossibleAddress); - uintptr_t address = memoized.load(std::memory_order_relaxed); - if (address != kImpossibleAddress) { - return reinterpret_cast<const unsigned char *>(address); - } - - address = reinterpret_cast<uintptr_t>(nullptr); - -#if ABSL_HAVE_VDSO_SUPPORT - absl::debugging_internal::VDSOSupport vdso; - if (vdso.IsPresent()) { - absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info; - // Symbol versioning pulled from arch/riscv/kernel/vdso/vdso.lds at v5.10. - auto lookup = [&](int type) { - return vdso.LookupSymbol("__vdso_rt_sigreturn", "LINUX_4.15", type, - &symbol_info); - }; - if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) || - symbol_info.address == nullptr) { - // Unexpected: VDSO is present, yet the expected symbol is missing or - // null. - assert(false && "VDSO is present, but doesn't have expected symbol"); - } else { - if (reinterpret_cast<uintptr_t>(symbol_info.address) != - kImpossibleAddress) { - address = reinterpret_cast<uintptr_t>(symbol_info.address); - } else { - assert(false && "VDSO returned invalid address"); - } - } - } -#endif - - memoized.store(address, std::memory_order_relaxed); - return reinterpret_cast<const unsigned char *>(address); -} -#endif // __linux__ - // Compute the size of a stack frame in [low..high). We assume that low < high. // Return size of kUnknownFrameSize. template <typename T> @@ -96,7 +54,8 @@ static inline uintptr_t ComputeStackFrameSize(const T *low, const T *high) { template <bool STRICT_UNWINDING, bool WITH_CONTEXT> ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. -static void ** NextStackFrame(void **old_frame_pointer, const void *uc) { +static void ** NextStackFrame(void **old_frame_pointer, const void *uc, + const std::pair<size_t, size_t> range) { // . // . // . @@ -114,55 +73,43 @@ static void ** NextStackFrame(void **old_frame_pointer, const void *uc) { // $sp ->| ... | // +----------------+ void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]); - bool check_frame_size = true; - -#if defined(__linux__) - if (WITH_CONTEXT && uc != nullptr) { - // Check to see if next frame's return address is __kernel_rt_sigreturn. - if (old_frame_pointer[-1] == GetKernelRtSigreturnAddress()) { - const ucontext_t *ucv = static_cast<const ucontext_t *>(uc); - // old_frame_pointer is not suitable for unwinding, look at ucontext to - // discover frame pointer before signal. - // - // RISCV ELF psABI has the frame pointer at x8/fp/s0. - // -- RISCV psABI Table 18.2 - void **const pre_signal_frame_pointer = - reinterpret_cast<void **>(ucv->uc_mcontext.__gregs[8]); - - // Check the alleged frame pointer is actually readable. This is to - // prevent "double fault" in case we hit the first fault due to stack - // corruption. - if (!absl::debugging_internal::AddressIsReadable( - pre_signal_frame_pointer)) - return nullptr; - - // Alleged frame pointer is readable, use it for further unwinding. - new_frame_pointer = pre_signal_frame_pointer; - - // Skip frame size check if we return from a signal. We may be using an - // alterate stack for signals. - check_frame_size = false; - } - } -#endif + uintptr_t frame_pointer = reinterpret_cast<uintptr_t>(new_frame_pointer); // The RISCV ELF psABI mandates that the stack pointer is always 16-byte // aligned. - // FIXME(abdulras) this doesn't hold for ILP32E which only mandates a 4-byte + // TODO(#1236) this doesn't hold for ILP32E which only mandates a 4-byte // alignment. - if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 15) != 0) + if (frame_pointer & 15) return nullptr; + // If the new frame pointer matches the signal context, avoid terminating + // early to deal with alternate signal stacks. + if (WITH_CONTEXT) + if (const ucontext_t *ucv = static_cast<const ucontext_t *>(uc)) + // RISCV ELF psABI has the frame pointer at x8/fp/s0. + // -- RISCV psABI Table 18.2 + if (ucv->uc_mcontext.__gregs[8] == frame_pointer) + return new_frame_pointer; + // 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 = - ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); - if (frame_size == kUnknownFrameSize || frame_size > max_size) + const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; + const uintptr_t frame_size = + ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); + if (frame_size == kUnknownFrameSize) { + if (STRICT_UNWINDING) + return nullptr; + + // In non-strict mode permit non-contiguous stacks (e.g. alternate signal + // frame handling). + if (reinterpret_cast<uintptr_t>(new_frame_pointer) < range.first || + reinterpret_cast<uintptr_t>(new_frame_pointer) > range.second) return nullptr; } + if (frame_size > max_size) + return nullptr; + return new_frame_pointer; } @@ -180,6 +127,12 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, #error reading stack pointer not yet supported on this platform #endif + std::pair<size_t, size_t> stack = { + // assume that the first page is not the stack. + static_cast<size_t>(sysconf(_SC_PAGESIZE)), + std::numeric_limits<size_t>::max() - sizeof(void *) + }; + int n = 0; void *return_address = nullptr; while (frame_pointer && n < max_depth) { @@ -190,7 +143,8 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, // 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); + NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, + stack); if (skip_count > 0) { skip_count--; @@ -217,7 +171,8 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, num_dropped_frames++; } frame_pointer = - NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp); + NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, + stack); } *min_dropped_frames = num_dropped_frames; } diff --git a/absl/debugging/internal/stacktrace_win32-inl.inc b/absl/debugging/internal/stacktrace_win32-inl.inc index 1c666c8b..ef2b973e 100644 --- a/absl/debugging/internal/stacktrace_win32-inl.inc +++ b/absl/debugging/internal/stacktrace_win32-inl.inc @@ -63,11 +63,12 @@ static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn = template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, const void*, int* min_dropped_frames) { - int n = 0; - if (!RtlCaptureStackBackTrace_fn) { - // can't find a stacktrace with no function to call + USHORT n = 0; + if (!RtlCaptureStackBackTrace_fn || skip_count < 0 || max_depth < 0) { + // can't get a stacktrace with no function/invalid args } else { - n = (int)RtlCaptureStackBackTrace_fn(skip_count + 2, max_depth, result, 0); + n = RtlCaptureStackBackTrace_fn(static_cast<ULONG>(skip_count) + 2, + static_cast<ULONG>(max_depth), result, 0); } if (IS_STACK_FRAMES) { // No implementation for finding out the stack frame sizes yet. diff --git a/absl/debugging/internal/stacktrace_x86-inl.inc b/absl/debugging/internal/stacktrace_x86-inl.inc index 1b5d8235..7b26464e 100644 --- a/absl/debugging/internal/stacktrace_x86-inl.inc +++ b/absl/debugging/internal/stacktrace_x86-inl.inc @@ -29,14 +29,13 @@ #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" #include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems #include "absl/debugging/stacktrace.h" -#include "absl/base/internal/raw_logging.h" - using absl::debugging_internal::AddressIsReadable; #if defined(__linux__) && defined(__i386__) @@ -113,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 @@ -140,13 +143,14 @@ static uintptr_t GetFP(const void *vuc) { // TODO(bcmills): -momit-leaf-frame-pointer is currently the default // behavior when building with clang. Talk to the C++ toolchain team about // fixing that. - if (bp >= sp && bp - sp <= kMaxFrameBytes) return bp; + if (bp >= sp && bp - sp <= kMaxFrameBytes) + return static_cast<uintptr_t>(bp); // If bp isn't a plausible frame pointer, return the stack pointer instead. // If we're lucky, it points to the start of a stack frame; otherwise, we'll // get one frame of garbage in the stack trace and fail the sanity check on // the next iteration. - return sp; + return static_cast<uintptr_t>(sp); } #endif return 0; @@ -258,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)) { @@ -310,8 +332,9 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, int n = 0; void **fp = reinterpret_cast<void **>(__builtin_frame_address(0)); - size_t stack_low = getpagesize(); // Assume that the first page is not stack. - size_t stack_high = std::numeric_limits<size_t>::max() - sizeof(void *); + // Assume that the first page is not stack. + size_t stack_low = static_cast<size_t>(getpagesize()); + size_t stack_high = kUnknownStackEnd; while (fp && n < max_depth) { if (*(fp + 1) == reinterpret_cast<void *>(0)) { @@ -327,7 +350,9 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, result[n] = *(fp + 1); if (IS_STACK_FRAMES) { if (next_fp > fp) { - sizes[n] = (uintptr_t)next_fp - (uintptr_t)fp; + sizes[n] = static_cast<int>( + reinterpret_cast<uintptr_t>(next_fp) - + reinterpret_cast<uintptr_t>(fp)); } else { // A frame-size of 0 is used to indicate unknown frame size. sizes[n] = 0; diff --git a/absl/debugging/internal/vdso_support.cc b/absl/debugging/internal/vdso_support.cc index 40eb055f..8a588eaf 100644 --- a/absl/debugging/internal/vdso_support.cc +++ b/absl/debugging/internal/vdso_support.cc @@ -193,8 +193,9 @@ long VDSOSupport::InitAndGetCPU(unsigned *cpu, // NOLINT(runtime/int) ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY int GetCPU() { unsigned cpu; - int ret_code = (*VDSOSupport::getcpu_fn_)(&cpu, nullptr, nullptr); - return ret_code == 0 ? cpu : ret_code; + long ret_code = // NOLINT(runtime/int) + (*VDSOSupport::getcpu_fn_)(&cpu, nullptr, nullptr); + return ret_code == 0 ? static_cast<int>(cpu) : static_cast<int>(ret_code); } } // namespace debugging_internal |