tor-browser

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

stack_consumption.cc (8338B)


      1 //
      2 // Copyright 2018 The Abseil Authors.
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      https://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 
     16 #include "absl/debugging/internal/stack_consumption.h"
     17 
     18 #ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
     19 
     20 #include <signal.h>
     21 #include <string.h>
     22 #include <sys/mman.h>
     23 #include <unistd.h>
     24 
     25 #include "absl/base/attributes.h"
     26 #include "absl/base/internal/raw_logging.h"
     27 
     28 #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
     29 #define MAP_ANONYMOUS MAP_ANON
     30 #endif
     31 
     32 namespace absl {
     33 ABSL_NAMESPACE_BEGIN
     34 namespace debugging_internal {
     35 namespace {
     36 
     37 // This code requires that we know the direction in which the stack
     38 // grows. It is commonly believed that this can be detected by putting
     39 // a variable on the stack and then passing its address to a function
     40 // that compares the address of this variable to the address of a
     41 // variable on the function's own stack. However, this is unspecified
     42 // behavior in C++: If two pointers p and q of the same type point to
     43 // different objects that are not members of the same object or
     44 // elements of the same array or to different functions, or if only
     45 // one of them is null, the results of p<q, p>q, p<=q, and p>=q are
     46 // unspecified. Therefore, instead we hardcode the direction of the
     47 // stack on platforms we know about.
     48 #if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \
     49    defined(__aarch64__) || defined(__riscv)
     50 constexpr bool kStackGrowsDown = true;
     51 #else
     52 #error Need to define kStackGrowsDown
     53 #endif
     54 
     55 // To measure the stack footprint of some code, we create a signal handler
     56 // (for SIGUSR2 say) that exercises this code on an alternate stack. This
     57 // alternate stack is initialized to some known pattern (0x55, 0x55, 0x55,
     58 // ...). We then self-send this signal, and after the signal handler returns,
     59 // look at the alternate stack buffer to see what portion has been touched.
     60 //
     61 // This trick gives us the the stack footprint of the signal handler.  But the
     62 // signal handler, even before the code for it is exercised, consumes some
     63 // stack already. We however only want the stack usage of the code inside the
     64 // signal handler. To measure this accurately, we install two signal handlers:
     65 // one that does nothing and just returns, and the user-provided signal
     66 // handler. The difference between the stack consumption of these two signals
     67 // handlers should give us the stack foorprint of interest.
     68 
     69 void EmptySignalHandler(int) {}
     70 
     71 // This is arbitrary value, and could be increase further, at the cost of
     72 // memset()ting it all to known sentinel value.
     73 constexpr int kAlternateStackSize = 64 << 10;  // 64KiB
     74 
     75 constexpr int kSafetyMargin = 32;
     76 constexpr char kAlternateStackFillValue = 0x55;
     77 
     78 // These helper functions look at the alternate stack buffer, and figure
     79 // out what portion of this buffer has been touched - this is the stack
     80 // consumption of the signal handler running on this alternate stack.
     81 // This function will return -1 if the alternate stack buffer has not been
     82 // touched. It will abort the program if the buffer has overflowed or is about
     83 // to overflow.
     84 int GetStackConsumption(const void* const altstack) {
     85  const char* begin;
     86  int increment;
     87  if (kStackGrowsDown) {
     88    begin = reinterpret_cast<const char*>(altstack);
     89    increment = 1;
     90  } else {
     91    begin = reinterpret_cast<const char*>(altstack) + kAlternateStackSize - 1;
     92    increment = -1;
     93  }
     94 
     95  for (int usage_count = kAlternateStackSize; usage_count > 0; --usage_count) {
     96    if (*begin != kAlternateStackFillValue) {
     97      ABSL_RAW_CHECK(usage_count <= kAlternateStackSize - kSafetyMargin,
     98                     "Buffer has overflowed or is about to overflow");
     99      return usage_count;
    100    }
    101    begin += increment;
    102  }
    103 
    104  ABSL_RAW_LOG(FATAL, "Unreachable code");
    105  return -1;
    106 }
    107 
    108 }  // namespace
    109 
    110 int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) {
    111  // The alt-signal-stack cannot be heap allocated because there is a
    112  // bug in glibc-2.2 where some signal handler setup code looks at the
    113  // current stack pointer to figure out what thread is currently running.
    114  // Therefore, the alternate stack must be allocated from the main stack
    115  // itself.
    116  void* altstack = mmap(nullptr, kAlternateStackSize, PROT_READ | PROT_WRITE,
    117                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    118  ABSL_RAW_CHECK(altstack != MAP_FAILED, "mmap() failed");
    119 
    120  // Set up the alt-signal-stack (and save the older one).
    121  stack_t sigstk;
    122  memset(&sigstk, 0, sizeof(sigstk));
    123  sigstk.ss_sp = altstack;
    124  sigstk.ss_size = kAlternateStackSize;
    125  sigstk.ss_flags = 0;
    126  stack_t old_sigstk;
    127  memset(&old_sigstk, 0, sizeof(old_sigstk));
    128  ABSL_RAW_CHECK(sigaltstack(&sigstk, &old_sigstk) == 0,
    129                 "sigaltstack() failed");
    130 
    131  // Set up SIGUSR1 and SIGUSR2 signal handlers (and save the older ones).
    132  struct sigaction sa;
    133  memset(&sa, 0, sizeof(sa));
    134  struct sigaction old_sa1, old_sa2;
    135  sigemptyset(&sa.sa_mask);
    136  sa.sa_flags = SA_ONSTACK;
    137 
    138  // SIGUSR1 maps to EmptySignalHandler.
    139  sa.sa_handler = EmptySignalHandler;
    140  ABSL_RAW_CHECK(sigaction(SIGUSR1, &sa, &old_sa1) == 0, "sigaction() failed");
    141 
    142  // SIGUSR2 maps to signal_handler.
    143  sa.sa_handler = signal_handler;
    144  ABSL_RAW_CHECK(sigaction(SIGUSR2, &sa, &old_sa2) == 0, "sigaction() failed");
    145 
    146  // Send SIGUSR1 signal and measure the stack consumption of the empty
    147  // signal handler.
    148  // The first signal might use more stack space. Run once and ignore the
    149  // results to get that out of the way.
    150  ABSL_RAW_CHECK(kill(getpid(), SIGUSR1) == 0, "kill() failed");
    151 
    152  memset(altstack, kAlternateStackFillValue, kAlternateStackSize);
    153  ABSL_RAW_CHECK(kill(getpid(), SIGUSR1) == 0, "kill() failed");
    154  int base_stack_consumption = GetStackConsumption(altstack);
    155 
    156  // Send SIGUSR2 signal and measure the stack consumption of signal_handler.
    157  ABSL_RAW_CHECK(kill(getpid(), SIGUSR2) == 0, "kill() failed");
    158  int signal_handler_stack_consumption = GetStackConsumption(altstack);
    159 
    160  // Now restore the old alt-signal-stack and signal handlers.
    161  if (old_sigstk.ss_sp == nullptr && old_sigstk.ss_size == 0 &&
    162      (old_sigstk.ss_flags & SS_DISABLE)) {
    163    // https://git.musl-libc.org/cgit/musl/commit/src/signal/sigaltstack.c?id=7829f42a2c8944555439380498ab8b924d0f2070
    164    // The original stack has ss_size==0 and ss_flags==SS_DISABLE, but some
    165    // versions of musl have a bug that rejects ss_size==0. Work around this by
    166    // setting ss_size to MINSIGSTKSZ, which should be ignored by the kernel
    167    // when SS_DISABLE is set.
    168    old_sigstk.ss_size = static_cast<size_t>(MINSIGSTKSZ);
    169  }
    170  ABSL_RAW_CHECK(sigaltstack(&old_sigstk, nullptr) == 0,
    171                 "sigaltstack() failed");
    172  ABSL_RAW_CHECK(sigaction(SIGUSR1, &old_sa1, nullptr) == 0,
    173                 "sigaction() failed");
    174  ABSL_RAW_CHECK(sigaction(SIGUSR2, &old_sa2, nullptr) == 0,
    175                 "sigaction() failed");
    176 
    177  ABSL_RAW_CHECK(munmap(altstack, kAlternateStackSize) == 0, "munmap() failed");
    178  if (signal_handler_stack_consumption != -1 && base_stack_consumption != -1) {
    179    return signal_handler_stack_consumption - base_stack_consumption;
    180  }
    181  return -1;
    182 }
    183 
    184 }  // namespace debugging_internal
    185 ABSL_NAMESPACE_END
    186 }  // namespace absl
    187 
    188 #else
    189 
    190 // https://github.com/abseil/abseil-cpp/issues/1465
    191 // CMake builds on Apple platforms error when libraries are empty.
    192 // Our CMake configuration can avoid this error on header-only libraries,
    193 // but since this library is conditionally empty, including a single
    194 // variable is an easy workaround.
    195 #ifdef __APPLE__
    196 namespace absl {
    197 ABSL_NAMESPACE_BEGIN
    198 namespace debugging_internal {
    199 extern const char kAvoidEmptyStackConsumptionLibraryWarning;
    200 const char kAvoidEmptyStackConsumptionLibraryWarning = 0;
    201 }  // namespace debugging_internal
    202 ABSL_NAMESPACE_END
    203 }  // namespace absl
    204 #endif  // __APPLE__
    205 
    206 #endif  // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION