tor-browser

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

MmapFaultHandler.cpp (4653B)


      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 "MmapFaultHandler.h"
      8 
      9 #if defined(XP_UNIX) && !defined(XP_DARWIN) && !defined(__wasi__)
     10 
     11 #  include "mozilla/Assertions.h"
     12 #  include "mozilla/Atomics.h"
     13 #  include "mozilla/ThreadLocal.h"
     14 #  include <signal.h>
     15 #  include <cstring>
     16 
     17 static MOZ_THREAD_LOCAL(MmapAccessScope*) sMmapAccessScope;
     18 
     19 static struct sigaction sPrevSIGBUSHandler;
     20 
     21 static void MmapSIGBUSHandler(int signum, siginfo_t* info, void* context) {
     22  MOZ_RELEASE_ASSERT(signum == SIGBUS);
     23 
     24  MmapAccessScope* mas = sMmapAccessScope.get();
     25 
     26  if (mas && mas->IsInsideBuffer(info->si_addr)) {
     27    // Temporarily instead of handling the signal, we crash intentionally and
     28    // send some diagnostic information to find out why the signal is received.
     29    mas->CrashWithInfo(info->si_addr);
     30 
     31    // The address is inside the buffer, handle the failure.
     32    siglongjmp(mas->mJmpBuf, signum);
     33  }
     34 
     35  // This signal is not caused by accessing region protected by MmapAccessScope.
     36  // Forward the signal to the next handler.
     37  if (sPrevSIGBUSHandler.sa_flags & SA_SIGINFO) {
     38    sPrevSIGBUSHandler.sa_sigaction(signum, info, context);
     39  } else if (sPrevSIGBUSHandler.sa_handler == SIG_DFL ||
     40             sPrevSIGBUSHandler.sa_handler == SIG_IGN) {
     41    // There is no next handler. Uninstalling our handler and returning will
     42    // cause a crash.
     43    sigaction(signum, &sPrevSIGBUSHandler, nullptr);
     44  } else {
     45    sPrevSIGBUSHandler.sa_handler(signum);
     46  }
     47 }
     48 
     49 mozilla::Atomic<bool> gSIGBUSHandlerInstalled(false);
     50 mozilla::Atomic<bool> gSIGBUSHandlerInstalling(false);
     51 
     52 void InstallMmapFaultHandler() {
     53  // This function is called from MmapAccessScope's constructor because there is
     54  // no single point where we could install the handler during startup. This
     55  // means that it's called quite often, so to minimize using of the mutex we
     56  // first check the atomic variable outside the lock.
     57  if (gSIGBUSHandlerInstalled) {
     58    return;
     59  }
     60 
     61  if (gSIGBUSHandlerInstalling.compareExchange(false, true)) {
     62    sMmapAccessScope.infallibleInit();
     63 
     64    struct sigaction busHandler;
     65    busHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
     66    busHandler.sa_sigaction = MmapSIGBUSHandler;
     67    sigemptyset(&busHandler.sa_mask);
     68    if (sigaction(SIGBUS, &busHandler, &sPrevSIGBUSHandler)) {
     69      MOZ_CRASH("Unable to install SIGBUS handler");
     70    }
     71 
     72    MOZ_ASSERT(!gSIGBUSHandlerInstalled);
     73    gSIGBUSHandlerInstalled = true;
     74  } else {
     75    // Just spin lock here. It should not take a substantial amount
     76    // of time, so a mutex would likely be a spin lock anyway, and
     77    // this avoids the need to new up a static mutex from within
     78    // mozglue/misc, which complicates things with
     79    // check_vanilla_allocations.py
     80    while (!gSIGBUSHandlerInstalled) {
     81    }
     82  }
     83 }
     84 
     85 MmapAccessScope::MmapAccessScope(void* aBuf, uint32_t aBufLen,
     86                                 const char* aFilename) {
     87  // Install signal handler if it wasn't installed yet.
     88  InstallMmapFaultHandler();
     89 
     90  // We'll handle the signal only if the crashing address is inside this buffer.
     91  mBuf = aBuf;
     92  mBufLen = aBufLen;
     93  mFilename = aFilename;
     94 
     95  SetThreadLocalScope();
     96 }
     97 
     98 MmapAccessScope::~MmapAccessScope() {
     99  MOZ_RELEASE_ASSERT(sMmapAccessScope.get() == this);
    100  sMmapAccessScope.set(mPreviousScope);
    101 }
    102 
    103 void MmapAccessScope::SetThreadLocalScope() {
    104  // mJmpBuf is set outside of this classs for reasons mentioned in the header
    105  // file, but we need to initialize the member here too to make Coverity happy.
    106  memset(mJmpBuf, 0, sizeof(sigjmp_buf));
    107 
    108  // If MmapAccessScopes are nested, save the previous one and restore it in
    109  // the destructor.
    110  mPreviousScope = sMmapAccessScope.get();
    111 
    112  // MmapAccessScope is now set up (except mJmpBuf for reasons mentioned in the
    113  // header file). Store the pointer in a thread-local variable sMmapAccessScope
    114  // so we can use it in the handler if the signal is triggered.
    115  sMmapAccessScope.set(this);
    116 }
    117 
    118 bool MmapAccessScope::IsInsideBuffer(void* aPtr) {
    119  return aPtr >= mBuf && aPtr < (void*)((char*)mBuf + mBufLen);
    120 }
    121 
    122 void MmapAccessScope::CrashWithInfo(void* aPtr) {
    123  // All we have is the buffer and the crashing address.
    124  MOZ_CRASH_UNSAFE_PRINTF(
    125      "SIGBUS received when accessing mmaped file [buffer=%p, "
    126      "buflen=%u, address=%p, filename=%s]",
    127      mBuf, mBufLen, aPtr, mFilename);
    128 }
    129 
    130 #endif