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