summaryrefslogtreecommitdiffstats
path: root/mozglue/misc/MmapFaultHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mozglue/misc/MmapFaultHandler.cpp')
-rw-r--r--mozglue/misc/MmapFaultHandler.cpp130
1 files changed, 130 insertions, 0 deletions
diff --git a/mozglue/misc/MmapFaultHandler.cpp b/mozglue/misc/MmapFaultHandler.cpp
new file mode 100644
index 0000000000..a73b9bd24b
--- /dev/null
+++ b/mozglue/misc/MmapFaultHandler.cpp
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MmapFaultHandler.h"
+
+#if defined(XP_UNIX) && !defined(XP_DARWIN) && !defined(__wasi__)
+
+# include "mozilla/Assertions.h"
+# include "mozilla/Atomics.h"
+# include "mozilla/ThreadLocal.h"
+# include <signal.h>
+# include <cstring>
+
+static MOZ_THREAD_LOCAL(MmapAccessScope*) sMmapAccessScope;
+
+static struct sigaction sPrevSIGBUSHandler;
+
+static void MmapSIGBUSHandler(int signum, siginfo_t* info, void* context) {
+ MOZ_RELEASE_ASSERT(signum == SIGBUS);
+
+ MmapAccessScope* mas = sMmapAccessScope.get();
+
+ if (mas && mas->IsInsideBuffer(info->si_addr)) {
+ // Temporarily instead of handling the signal, we crash intentionally and
+ // send some diagnostic information to find out why the signal is received.
+ mas->CrashWithInfo(info->si_addr);
+
+ // The address is inside the buffer, handle the failure.
+ siglongjmp(mas->mJmpBuf, signum);
+ }
+
+ // This signal is not caused by accessing region protected by MmapAccessScope.
+ // Forward the signal to the next handler.
+ if (sPrevSIGBUSHandler.sa_flags & SA_SIGINFO) {
+ sPrevSIGBUSHandler.sa_sigaction(signum, info, context);
+ } else if (sPrevSIGBUSHandler.sa_handler == SIG_DFL ||
+ sPrevSIGBUSHandler.sa_handler == SIG_IGN) {
+ // There is no next handler. Uninstalling our handler and returning will
+ // cause a crash.
+ sigaction(signum, &sPrevSIGBUSHandler, nullptr);
+ } else {
+ sPrevSIGBUSHandler.sa_handler(signum);
+ }
+}
+
+mozilla::Atomic<bool> gSIGBUSHandlerInstalled(false);
+mozilla::Atomic<bool> gSIGBUSHandlerInstalling(false);
+
+void InstallMmapFaultHandler() {
+ // This function is called from MmapAccessScope's constructor because there is
+ // no single point where we could install the handler during startup. This
+ // means that it's called quite often, so to minimize using of the mutex we
+ // first check the atomic variable outside the lock.
+ if (gSIGBUSHandlerInstalled) {
+ return;
+ }
+
+ if (gSIGBUSHandlerInstalling.compareExchange(false, true)) {
+ sMmapAccessScope.infallibleInit();
+
+ struct sigaction busHandler;
+ busHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
+ busHandler.sa_sigaction = MmapSIGBUSHandler;
+ sigemptyset(&busHandler.sa_mask);
+ if (sigaction(SIGBUS, &busHandler, &sPrevSIGBUSHandler)) {
+ MOZ_CRASH("Unable to install SIGBUS handler");
+ }
+
+ MOZ_ASSERT(!gSIGBUSHandlerInstalled);
+ gSIGBUSHandlerInstalled = true;
+ } else {
+ // Just spin lock here. It should not take a substantial amount
+ // of time, so a mutex would likely be a spin lock anyway, and
+ // this avoids the need to new up a static mutex from within
+ // mozglue/misc, which complicates things with
+ // check_vanilla_allocations.py
+ while (!gSIGBUSHandlerInstalled) {
+ }
+ }
+}
+
+MmapAccessScope::MmapAccessScope(void* aBuf, uint32_t aBufLen,
+ const char* aFilename) {
+ // Install signal handler if it wasn't installed yet.
+ InstallMmapFaultHandler();
+
+ // We'll handle the signal only if the crashing address is inside this buffer.
+ mBuf = aBuf;
+ mBufLen = aBufLen;
+ mFilename = aFilename;
+
+ SetThreadLocalScope();
+}
+
+MmapAccessScope::~MmapAccessScope() {
+ MOZ_RELEASE_ASSERT(sMmapAccessScope.get() == this);
+ sMmapAccessScope.set(mPreviousScope);
+}
+
+void MmapAccessScope::SetThreadLocalScope() {
+ // mJmpBuf is set outside of this classs for reasons mentioned in the header
+ // file, but we need to initialize the member here too to make Coverity happy.
+ memset(mJmpBuf, 0, sizeof(sigjmp_buf));
+
+ // If MmapAccessScopes are nested, save the previous one and restore it in
+ // the destructor.
+ mPreviousScope = sMmapAccessScope.get();
+
+ // MmapAccessScope is now set up (except mJmpBuf for reasons mentioned in the
+ // header file). Store the pointer in a thread-local variable sMmapAccessScope
+ // so we can use it in the handler if the signal is triggered.
+ sMmapAccessScope.set(this);
+}
+
+bool MmapAccessScope::IsInsideBuffer(void* aPtr) {
+ return aPtr >= mBuf && aPtr < (void*)((char*)mBuf + mBufLen);
+}
+
+void MmapAccessScope::CrashWithInfo(void* aPtr) {
+ // All we have is the buffer and the crashing address.
+ MOZ_CRASH_UNSAFE_PRINTF(
+ "SIGBUS received when accessing mmaped file [buffer=%p, "
+ "buflen=%u, address=%p, filename=%s]",
+ mBuf, mBufLen, aPtr, mFilename);
+}
+
+#endif