tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

stacktrace_test.cc (5331B)


      1 // Copyright 2023 The Abseil Authors.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      https://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include "absl/debugging/stacktrace.h"
     16 
     17 #include <stddef.h>
     18 #include <stdint.h>
     19 
     20 #include <algorithm>
     21 
     22 #include "gmock/gmock.h"
     23 #include "gtest/gtest.h"
     24 #include "absl/base/attributes.h"
     25 #include "absl/base/config.h"
     26 #include "absl/base/optimization.h"
     27 #include "absl/types/span.h"
     28 
     29 namespace {
     30 
     31 using ::testing::Contains;
     32 
     33 struct StackTrace {
     34  static constexpr int kStackCount = 64;
     35  int depth;
     36  void* result[kStackCount];
     37  uintptr_t frames[kStackCount];
     38  int sizes[kStackCount];
     39 };
     40 
     41 // This test is currently only known to pass on Linux x86_64/aarch64.
     42 #if defined(__linux__) && (defined(__x86_64__) || defined(__aarch64__))
     43 ABSL_ATTRIBUTE_NOINLINE void Unwind(void* p) {
     44  ABSL_ATTRIBUTE_UNUSED static void* volatile sink = p;
     45  constexpr int kSize = 16;
     46  void* stack[kSize];
     47  int frames[kSize];
     48  absl::GetStackTrace(stack, kSize, 0);
     49  absl::GetStackFrames(stack, frames, kSize, 0);
     50 }
     51 
     52 ABSL_ATTRIBUTE_NOINLINE void HugeFrame() {
     53  char buffer[1 << 20];
     54  Unwind(buffer);
     55  ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
     56 }
     57 
     58 TEST(StackTrace, HugeFrame) {
     59  // Ensure that the unwinder is not confused by very large stack frames.
     60  HugeFrame();
     61  ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
     62 }
     63 #endif
     64 
     65 #if ABSL_HAVE_BUILTIN(__builtin_frame_address)
     66 struct FrameInfo {
     67  const void* return_address;
     68  uintptr_t frame_address;
     69 };
     70 
     71 // Returns the canonical frame address and return address for the current stack
     72 // frame, while capturing the stack trace at the same time.
     73 // This performs any platform-specific adjustments necessary to convert from the
     74 // compiler built-ins to the expected API outputs.
     75 ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS     // May read random elements from stack.
     76    ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY  // May read random elements from stack.
     77        ABSL_ATTRIBUTE_NOINLINE static FrameInfo
     78        CaptureBacktraceNoInline(StackTrace& backtrace) {
     79  FrameInfo result;
     80  result.return_address = __builtin_return_address(0);
     81  // Large enough to cover all realistic slots the return address could be in
     82  const int kMaxReturnAddressIndex = 5;
     83  void* const* bfa = static_cast<void* const*>(__builtin_frame_address(0));
     84  backtrace.depth = absl::internal_stacktrace::GetStackFramesWithContext(
     85      backtrace.result, backtrace.frames, backtrace.sizes,
     86      StackTrace::kStackCount, /*skip_count=*/0,
     87      /*uc=*/nullptr, /*min_dropped_frames=*/nullptr);
     88  // Make sure the return address is at a reasonable location in the frame
     89  ptrdiff_t i;
     90  for (i = 0; i < kMaxReturnAddressIndex; ++i) {
     91    // Avoid std::find() here, since it lacks no-sanitize attributes.
     92    if (bfa[i] == result.return_address) {
     93      break;
     94    }
     95  }
     96  result.frame_address =
     97      i < kMaxReturnAddressIndex
     98          ? reinterpret_cast<uintptr_t>(
     99                bfa + i + 1 /* get the Canonical Frame Address (CFA) */)
    100          : 0;
    101  return result;
    102 }
    103 
    104 TEST(StackTrace, CanonicalFrameAddresses) {
    105  // Now capture a stack trace and verify that the return addresses and frame
    106  // addresses line up for one frame.
    107  StackTrace backtrace;
    108  const auto [return_address, frame_address] =
    109      CaptureBacktraceNoInline(backtrace);
    110  auto return_addresses = absl::MakeSpan(backtrace.result)
    111                              .subspan(0, static_cast<size_t>(backtrace.depth));
    112  auto frame_addresses = absl::MakeSpan(backtrace.frames)
    113                             .subspan(0, static_cast<size_t>(backtrace.depth));
    114 
    115  // Many platforms don't support this by default.
    116  bool support_is_expected = false;
    117 
    118  if (support_is_expected) {
    119    // If all zeros were returned, that is valid per the function's contract.
    120    // It just means we don't support returning frame addresses on this
    121    // platform.
    122    bool supported = static_cast<size_t>(std::count(frame_addresses.begin(),
    123                                                    frame_addresses.end(), 0)) <
    124                     frame_addresses.size();
    125    EXPECT_TRUE(supported);
    126    if (supported) {
    127      ASSERT_TRUE(frame_address)
    128          << "unable to obtain frame address corresponding to return address";
    129      EXPECT_THAT(return_addresses, Contains(return_address).Times(1));
    130      EXPECT_THAT(frame_addresses, Contains(frame_address).Times(1));
    131      ptrdiff_t ifound = std::find(return_addresses.begin(),
    132                                   return_addresses.end(), return_address) -
    133                         return_addresses.begin();
    134      // Make sure we found the frame in the first place.
    135      ASSERT_LT(ifound, backtrace.depth);
    136      // Make sure the frame address actually corresponds to the return
    137      // address.
    138      EXPECT_EQ(frame_addresses[static_cast<size_t>(ifound)], frame_address);
    139      // Make sure the addresses only appear once.
    140    }
    141  }
    142 }
    143 #endif
    144 
    145 }  // namespace