tor-browser

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

NativeStack.cpp (6923B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "util/NativeStack.h"
      8 
      9 #include "mozilla/Assertions.h"  // MOZ_ASSERT, MOZ_RELEASE_ASSERT, MOZ_CRASH
     10 
     11 #ifdef XP_WIN
     12 #  include "util/WindowsWrapper.h"
     13 #elif defined(__wasi__)
     14 // Nothing
     15 #elif defined(XP_DARWIN) || defined(DARWIN) || defined(XP_UNIX)
     16 #  include <pthread.h>
     17 #  if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
     18 #    include <pthread_np.h>
     19 #  endif
     20 #  if defined(SOLARIS) || defined(AIX)
     21 #    include <ucontext.h>
     22 #  endif
     23 #  if defined(ANDROID) && !defined(__aarch64__)
     24 #    include <sys/types.h>
     25 #    include <unistd.h>
     26 #  endif
     27 #  if defined(XP_LINUX) && !defined(ANDROID) && defined(__GLIBC__)
     28 #    include <dlfcn.h>
     29 #    include <sys/syscall.h>
     30 #    include <sys/types.h>
     31 #    include <unistd.h>
     32 #    define gettid() static_cast<pid_t>(syscall(__NR_gettid))
     33 #  endif
     34 #else
     35 #  error "Unsupported platform"
     36 #endif
     37 
     38 #include "js/friend/StackLimits.h"  // JS_STACK_GROWTH_DIRECTION
     39 
     40 #if defined(XP_WIN)
     41 
     42 void* js::GetNativeStackBaseImpl() {
     43  PNT_TIB pTib = reinterpret_cast<PNT_TIB>(NtCurrentTeb());
     44  return static_cast<void*>(pTib->StackBase);
     45 }
     46 
     47 #elif defined(SOLARIS)
     48 
     49 static_assert(JS_STACK_GROWTH_DIRECTION < 0);
     50 
     51 void* js::GetNativeStackBaseImpl() {
     52  stack_t st;
     53  stack_getbounds(&st);
     54  return static_cast<char*>(st.ss_sp) + st.ss_size;
     55 }
     56 
     57 #elif defined(AIX)
     58 
     59 static_assert(JS_STACK_GROWTH_DIRECTION < 0);
     60 
     61 void* js::GetNativeStackBaseImpl() {
     62  ucontext_t context;
     63  getcontext(&context);
     64  return static_cast<char*>(context.uc_stack.ss_sp) + context.uc_stack.ss_size;
     65 }
     66 
     67 #elif defined(XP_LINUX) && !defined(ANDROID) && defined(__GLIBC__)
     68 void* js::GetNativeStackBaseImpl() {
     69  // On the main thread, get stack base from glibc's __libc_stack_end rather
     70  // than pthread APIs to avoid filesystem calls /proc/self/maps.  Non-main
     71  // threads spawned with pthreads can read this information directly from their
     72  // pthread struct, but the main thread must go parse /proc/self/maps to figure
     73  // the mapped stack address space ranges.  We want to avoid reading from
     74  // /proc/ so that firefox can run in sandboxed environments where /proc may
     75  // not be mounted.
     76  if (gettid() == getpid()) {
     77    void** pLibcStackEnd = (void**)dlsym(RTLD_DEFAULT, "__libc_stack_end");
     78 
     79    // If __libc_stack_end is not found, architecture specific frame pointer
     80    // hopping will need to be implemented.
     81    MOZ_RELEASE_ASSERT(
     82        pLibcStackEnd,
     83        "__libc_stack_end unavailable, unable to setup stack range for JS");
     84    void* stackBase = *pLibcStackEnd;
     85    MOZ_RELEASE_ASSERT(
     86        stackBase, "invalid stack base, unable to setup stack range for JS");
     87 
     88    // We don't need to fix stackBase, as it already roughly points to beginning
     89    // of the stack
     90    return stackBase;
     91  }
     92 
     93  // Non-main threads have the required info stored in memory, so no filesystem
     94  // calls are made.
     95  pthread_t thread = pthread_self();
     96  pthread_attr_t sattr;
     97  pthread_attr_init(&sattr);
     98  int rc = pthread_getattr_np(thread, &sattr);
     99  MOZ_RELEASE_ASSERT(rc == 0, "pthread_getattr_np failed");
    100 
    101  // stackBase will be the *lowest* address on all architectures.
    102  void* stackBase = nullptr;
    103  size_t stackSize = 0;
    104  rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize);
    105  MOZ_RELEASE_ASSERT(rc == 0,
    106                     "call to pthread_attr_getstack failed, unable to setup "
    107                     "stack range for JS");
    108  MOZ_RELEASE_ASSERT(stackBase,
    109                     "invalid stack base, unable to setup stack range for JS");
    110  pthread_attr_destroy(&sattr);
    111 
    112 #  if JS_STACK_GROWTH_DIRECTION > 0
    113  return stackBase;
    114 #  else
    115  return static_cast<char*>(stackBase) + stackSize;
    116 #  endif
    117 }
    118 
    119 #elif defined(__wasi__)
    120 
    121 // Since we rearrange the layout for wasi via --stack-first flag for the linker
    122 // the final layout is: 0x00 | <- stack | data | heap -> |.
    123 static void* const NativeStackBase = __builtin_frame_address(0);
    124 
    125 void* js::GetNativeStackBaseImpl() {
    126  MOZ_ASSERT(JS_STACK_GROWTH_DIRECTION < 0);
    127  return NativeStackBase;
    128 }
    129 
    130 #else  // __wasi__
    131 
    132 void* js::GetNativeStackBaseImpl() {
    133  pthread_t thread = pthread_self();
    134 #  if defined(XP_DARWIN) || defined(DARWIN)
    135  return pthread_get_stackaddr_np(thread);
    136 
    137 #  else
    138  pthread_attr_t sattr;
    139  pthread_attr_init(&sattr);
    140 #    if defined(__OpenBSD__)
    141  stack_t ss;
    142 #    elif defined(PTHREAD_NP_H) || defined(_PTHREAD_NP_H_) || defined(NETBSD)
    143  /* e.g. on FreeBSD 4.8 or newer, neundorf@kde.org */
    144  pthread_attr_get_np(thread, &sattr);
    145 #    else
    146  /*
    147   * FIXME: this function is non-portable;
    148   * other POSIX systems may have different np alternatives
    149   */
    150  MOZ_RELEASE_ASSERT(pthread_getattr_np(thread, &sattr) == 0,
    151                     "pthread_getattr_np failed");
    152 #    endif
    153 
    154  void* stackBase = 0;
    155  size_t stackSize = 0;
    156  int rc;
    157 #    if defined(__OpenBSD__)
    158  rc = pthread_stackseg_np(pthread_self(), &ss);
    159  stackBase = (void*)((size_t)ss.ss_sp - ss.ss_size);
    160  stackSize = ss.ss_size;
    161 #    elif defined(ANDROID) && !defined(__aarch64__)
    162  if (gettid() == getpid()) {
    163    // bionic's pthread_attr_getstack prior to API 21 doesn't tell the truth
    164    // for the main thread (see bug 846670). So we scan /proc/self/maps to
    165    // find the segment which contains the stack.
    166    rc = -1;
    167 
    168    // Put the string on the stack, otherwise there is the danger that it
    169    // has not been decompressed by the the on-demand linker. Bug 1165460.
    170    //
    171    // The volatile keyword should stop the compiler from trying to omit
    172    // the stack copy in the future (hopefully).
    173    volatile char path[] = "/proc/self/maps";
    174    FILE* fs = fopen((const char*)path, "r");
    175 
    176    if (fs) {
    177      char line[100];
    178      unsigned long stackAddr = (unsigned long)&sattr;
    179      while (fgets(line, sizeof(line), fs) != nullptr) {
    180        unsigned long stackStart;
    181        unsigned long stackEnd;
    182        if (sscanf(line, "%lx-%lx ", &stackStart, &stackEnd) == 2 &&
    183            stackAddr >= stackStart && stackAddr < stackEnd) {
    184          stackBase = (void*)stackStart;
    185          stackSize = stackEnd - stackStart;
    186          rc = 0;
    187          break;
    188        }
    189      }
    190      fclose(fs);
    191    }
    192  } else {
    193    // For non main-threads pthread allocates the stack itself so it tells
    194    // the truth.
    195    rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize);
    196  }
    197 #    else
    198  rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize);
    199 #    endif
    200  if (rc) {
    201    MOZ_CRASH();
    202  }
    203  MOZ_ASSERT(stackBase);
    204  pthread_attr_destroy(&sattr);
    205 
    206 #    if JS_STACK_GROWTH_DIRECTION > 0
    207  return stackBase;
    208 #    else
    209  return static_cast<char*>(stackBase) + stackSize;
    210 #    endif
    211 #  endif
    212 }
    213 
    214 #endif /* !XP_WIN */