tor-browser

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

stacktrace_riscv-inl.inc (7269B)


      1 // Copyright 2021 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 #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_
     16 #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_
     17 
     18 // Generate stack trace for riscv
     19 
     20 #include <sys/ucontext.h>
     21 
     22 #include "absl/base/config.h"
     23 #include "absl/debugging/internal/addresses.h"
     24 #if defined(__linux__)
     25 #include <sys/mman.h>
     26 #include <ucontext.h>
     27 #include <unistd.h>
     28 #endif
     29 
     30 #include <atomic>
     31 #include <cassert>
     32 #include <cstdint>
     33 #include <iostream>
     34 #include <limits>
     35 #include <utility>
     36 
     37 #include "absl/base/attributes.h"
     38 #include "absl/debugging/stacktrace.h"
     39 
     40 static constexpr ptrdiff_t kUnknownFrameSize = 0;
     41 
     42 // Compute the size of a stack frame in [low..high).  We assume that low < high.
     43 // Return size of kUnknownFrameSize.
     44 template <typename T>
     45 static inline ptrdiff_t ComputeStackFrameSize(const T *low, const T *high) {
     46  const char *low_char_ptr = reinterpret_cast<const char *>(low);
     47  const char *high_char_ptr = reinterpret_cast<const char *>(high);
     48  return low < high ? static_cast<ptrdiff_t>(high_char_ptr - low_char_ptr)
     49                    : kUnknownFrameSize;
     50 }
     51 
     52 // Given a pointer to a stack frame, locate and return the calling stackframe,
     53 // or return null if no stackframe can be found. Perform sanity checks (the
     54 // strictness of which is controlled by the boolean parameter
     55 // "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
     56 template <bool STRICT_UNWINDING, bool WITH_CONTEXT>
     57 ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS  // May read random elements from stack.
     58 ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY   // May read random elements from stack.
     59 static void ** NextStackFrame(void **old_frame_pointer, const void *uc,
     60                              const std::pair<size_t, size_t> range) {
     61  //               .
     62  //               .
     63  //               .
     64  //   +-> +----------------+
     65  //   |   | return address |
     66  //   |   |   previous fp  |
     67  //   |   |      ...       |
     68  //   |   +----------------+ <-+
     69  //   |   | return address |   |
     70  //   +---|-  previous fp  |   |
     71  //       |      ...       |   |
     72  // $fp ->|----------------+   |
     73  //       | return address |   |
     74  //       |   previous fp -|---+
     75  // $sp ->|      ...       |
     76  //       +----------------+
     77  void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]);
     78  uintptr_t frame_pointer = reinterpret_cast<uintptr_t>(new_frame_pointer);
     79 
     80  // The RISCV ELF psABI mandates that the stack pointer is always 16-byte
     81  // aligned.
     82  // TODO(#1236) this doesn't hold for ILP32E which only mandates a 4-byte
     83  // alignment.
     84  if (frame_pointer & 15)
     85    return nullptr;
     86 
     87  // If the new frame pointer matches the signal context, avoid terminating
     88  // early to deal with alternate signal stacks.
     89  if (WITH_CONTEXT)
     90    if (const ucontext_t *ucv = static_cast<const ucontext_t *>(uc))
     91      // RISCV ELF psABI has the frame pointer at x8/fp/s0.
     92      // -- RISCV psABI Table 18.2
     93      if (ucv->uc_mcontext.__gregs[8] == frame_pointer)
     94        return new_frame_pointer;
     95 
     96  // Check frame size.  In strict mode, we assume frames to be under 100,000
     97  // bytes.  In non-strict mode, we relax the limit to 1MB.
     98  const ptrdiff_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
     99  const ptrdiff_t frame_size =
    100      ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
    101  if (frame_size == kUnknownFrameSize) {
    102    if (STRICT_UNWINDING)
    103      return nullptr;
    104 
    105    // In non-strict mode permit non-contiguous stacks (e.g. alternate signal
    106    // frame handling).
    107    if (reinterpret_cast<uintptr_t>(new_frame_pointer) < range.first ||
    108        reinterpret_cast<uintptr_t>(new_frame_pointer) > range.second)
    109      return nullptr;
    110  }
    111 
    112  if (frame_size > max_size)
    113    return nullptr;
    114 
    115  return new_frame_pointer;
    116 }
    117 
    118 template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
    119 ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS  // May read random elements from stack.
    120 ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY   // May read random elements from stack.
    121 static int UnwindImpl(void **result, uintptr_t *frames, int *sizes,
    122                      int max_depth, int skip_count, const void *ucp,
    123                      int *min_dropped_frames) {
    124  // The `frame_pointer` that is computed here points to the top of the frame.
    125  // The two words preceding the address are the return address and the previous
    126  // frame pointer.
    127 #if defined(__GNUC__)
    128  void **frame_pointer = reinterpret_cast<void **>(__builtin_frame_address(0));
    129 #else
    130 #error reading stack pointer not yet supported on this platform
    131 #endif
    132 
    133  std::pair<size_t, size_t> stack = {
    134      // assume that the first page is not the stack.
    135      static_cast<size_t>(sysconf(_SC_PAGESIZE)),
    136      std::numeric_limits<size_t>::max() - sizeof(void *)
    137  };
    138 
    139  int n = 0;
    140  void *return_address = nullptr;
    141  while (frame_pointer && n < max_depth) {
    142    return_address = frame_pointer[-1];
    143 
    144    // The absl::GetStackFrames routine is called when we are in some
    145    // informational context (the failure signal handler for example).  Use the
    146    // non-strict unwinding rules to produce a stack trace that is as complete
    147    // as possible (even if it contains a few bogus entries in some rare cases).
    148    void **next_frame_pointer =
    149        NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp,
    150                                                          stack);
    151 
    152    if (skip_count > 0) {
    153      skip_count--;
    154    } else {
    155      result[n] = return_address;
    156      if (IS_STACK_FRAMES) {
    157        // NextStackFrame() has already checked that frame size fits to int
    158        if (frames != nullptr) {
    159          frames[n] =
    160              absl::debugging_internal::StripPointerMetadata(frame_pointer);
    161        }
    162        if (sizes != nullptr) {
    163          sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer);
    164        }
    165      }
    166      n++;
    167    }
    168 
    169    frame_pointer = next_frame_pointer;
    170  }
    171 
    172  if (min_dropped_frames != nullptr) {
    173    // Implementation detail: we clamp the max of frames we are willing to
    174    // count, so as not to spend too much time in the loop below.
    175    const int kMaxUnwind = 200;
    176    int num_dropped_frames = 0;
    177    for (int j = 0; frame_pointer != nullptr && j < kMaxUnwind; j++) {
    178      if (skip_count > 0) {
    179        skip_count--;
    180      } else {
    181        num_dropped_frames++;
    182      }
    183      frame_pointer =
    184          NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp,
    185                                                            stack);
    186    }
    187    *min_dropped_frames = num_dropped_frames;
    188  }
    189 
    190  return n;
    191 }
    192 
    193 namespace absl {
    194 ABSL_NAMESPACE_BEGIN
    195 namespace debugging_internal {
    196 bool StackTraceWorksForTest() { return true; }
    197 }  // namespace debugging_internal
    198 ABSL_NAMESPACE_END
    199 }  // namespace absl
    200 
    201 #endif