Bug 1799929 [wpt PR 36892] - Workaround WebKit differences with load events & documen...
[gecko.git] / mozglue / misc / MmapFaultHandler.cpp
bloba73b9bd24b7134480ea52c013c671281f1d79366
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/. */
7 #include "MmapFaultHandler.h"
9 #if defined(XP_UNIX) && !defined(XP_DARWIN) && !defined(__wasi__)
11 # include "mozilla/Assertions.h"
12 # include "mozilla/Atomics.h"
13 # include "mozilla/ThreadLocal.h"
14 # include <signal.h>
15 # include <cstring>
17 static MOZ_THREAD_LOCAL(MmapAccessScope*) sMmapAccessScope;
19 static struct sigaction sPrevSIGBUSHandler;
21 static void MmapSIGBUSHandler(int signum, siginfo_t* info, void* context) {
22 MOZ_RELEASE_ASSERT(signum == SIGBUS);
24 MmapAccessScope* mas = sMmapAccessScope.get();
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);
31 // The address is inside the buffer, handle the failure.
32 siglongjmp(mas->mJmpBuf, signum);
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);
49 mozilla::Atomic<bool> gSIGBUSHandlerInstalled(false);
50 mozilla::Atomic<bool> gSIGBUSHandlerInstalling(false);
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;
61 if (gSIGBUSHandlerInstalling.compareExchange(false, true)) {
62 sMmapAccessScope.infallibleInit();
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");
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) {
85 MmapAccessScope::MmapAccessScope(void* aBuf, uint32_t aBufLen,
86 const char* aFilename) {
87 // Install signal handler if it wasn't installed yet.
88 InstallMmapFaultHandler();
90 // We'll handle the signal only if the crashing address is inside this buffer.
91 mBuf = aBuf;
92 mBufLen = aBufLen;
93 mFilename = aFilename;
95 SetThreadLocalScope();
98 MmapAccessScope::~MmapAccessScope() {
99 MOZ_RELEASE_ASSERT(sMmapAccessScope.get() == this);
100 sMmapAccessScope.set(mPreviousScope);
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));
108 // If MmapAccessScopes are nested, save the previous one and restore it in
109 // the destructor.
110 mPreviousScope = sMmapAccessScope.get();
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);
118 bool MmapAccessScope::IsInsideBuffer(void* aPtr) {
119 return aPtr >= mBuf && aPtr < (void*)((char*)mBuf + mBufLen);
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);
130 #endif