Bug 1338574 - Part 1: Add MOZ_CRASH_UNSAFE_OOL and MOZ_CRASH_UNSAFE_PRINTF to crash with a runtime generated explanation string. r=froydnj
authorEmanuel Hoogeveen <emanuel.hoogeveen@gmail.com>
Tue, 21 Feb 2017 18:01:41 +0100
changeset 393137 7fdb44e79e51279ac2076040f6bd7bc341b97c23
parent 393136 00d0810a4e6bc524973c12ac393e9a029ab80db2
child 393138 253f65ad681fdc0ebc8894b4ba6512598adeabbb
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1338574
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1338574 - Part 1: Add MOZ_CRASH_UNSAFE_OOL and MOZ_CRASH_UNSAFE_PRINTF to crash with a runtime generated explanation string. r=froydnj
memory/replace/logalloc/replay/moz.build
mfbt/Assertions.cpp
mfbt/Assertions.h
--- a/memory/replace/logalloc/replay/moz.build
+++ b/memory/replace/logalloc/replay/moz.build
@@ -16,9 +16,12 @@ LOCAL_INCLUDES += [
     '..',
 ]
 
 # Link replace-malloc and the default allocator.
 USE_LIBS += [
     'memory',
 ]
 
+# The memory library defines this, so it's needed here too.
+DEFINES['IMPL_MFBT'] = True
+
 DISABLE_STL_WRAPPING = True
--- a/mfbt/Assertions.cpp
+++ b/mfbt/Assertions.cpp
@@ -1,17 +1,66 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "mozilla/Types.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+
+#include <stdarg.h>
+
+MOZ_BEGIN_EXTERN_C
 
 /*
  * The crash reason is defined as a global variable here rather than in the
  * crash reporter itself to make it available to all code, even libraries like
  * JS that don't link with the crash reporter directly. This value will only
  * be consumed if the crash reporter is used by the target application.
  */
+MFBT_DATA const char* gMozCrashReason = nullptr;
 
-MOZ_BEGIN_EXTERN_C
-MOZ_EXPORT const char* gMozCrashReason = nullptr;
+#ifndef DEBUG
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
+MOZ_CrashOOL(int aLine, const char* aReason)
+#else
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
+MOZ_CrashOOL(const char* aFilename, int aLine, const char* aReason)
+#endif
+{
+#ifdef DEBUG
+  MOZ_ReportCrash(aReason, aFilename, aLine);
+#endif
+  MOZ_CRASH_ANNOTATE(aReason);
+  MOZ_REALLY_CRASH(aLine);
+}
+
+static char sPrintfCrashReason[sPrintfCrashReasonSize] = {};
+static mozilla::Atomic<bool> sCrashing(false);
+
+#ifndef DEBUG
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(2, 3) void
+MOZ_CrashPrintf(int aLine, const char* aFormat, ...)
+#else
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(3, 4) void
+MOZ_CrashPrintf(const char* aFilename, int aLine, const char* aFormat, ...)
+#endif
+{
+  if (!sCrashing.compareExchange(false, true)) {
+    // In the unlikely event of a race condition, skip
+    // setting the crash reason and just crash safely.
+    MOZ_REALLY_CRASH(aLine);
+  }
+  va_list aArgs;
+  va_start(aArgs, aFormat);
+  int ret = vsnprintf(sPrintfCrashReason, sPrintfCrashReasonSize,
+                      aFormat, aArgs);
+  va_end(aArgs);
+  MOZ_RELEASE_ASSERT(ret >= 0 && size_t(ret) < sPrintfCrashReasonSize,
+    "Could not write the explanation string to the supplied buffer!");
+#ifdef DEBUG
+  MOZ_ReportCrash(sPrintfCrashReason, aFilename, aLine);
+#endif
+  MOZ_CRASH_ANNOTATE(sPrintfCrashReason);
+  MOZ_REALLY_CRASH(aLine);
+}
+
 MOZ_END_EXTERN_C
--- a/mfbt/Assertions.h
+++ b/mfbt/Assertions.h
@@ -266,16 +266,68 @@ static MOZ_COLD MOZ_NORETURN MOZ_NEVER_I
 #  define MOZ_CRASH(...) \
      do { \
        MOZ_ReportCrash("" __VA_ARGS__, __FILE__, __LINE__); \
        MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \
        MOZ_REALLY_CRASH(__LINE__); \
      } while (0)
 #endif
 
+/*
+ * MOZ_CRASH_UNSAFE_OOL(explanation-string) can be used if the explanation
+ * string cannot be a string literal (but no other processing needs to be done
+ * on it). A regular MOZ_CRASH() is preferred wherever possible, as passing
+ * arbitrary strings from a potentially compromised process is not without risk.
+ * If the string being passed is the result of a printf-style function,
+ * consider using MOZ_CRASH_UNSAFE_PRINTF instead.
+ */
+#ifndef DEBUG
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
+MOZ_CrashOOL(int aLine, const char* aReason);
+#  define MOZ_CRASH_UNSAFE_OOL(reason) MOZ_CrashOOL(__LINE__, reason)
+#else
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
+MOZ_CrashOOL(const char* aFilename, int aLine, const char* aReason);
+#  define MOZ_CRASH_UNSAFE_OOL(reason) MOZ_CrashOOL(__FILE__, __LINE__, reason)
+#endif
+
+static const size_t sPrintfMaxArgs = 4;
+static const size_t sPrintfCrashReasonSize = 1024;
+
+#ifndef DEBUG
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(2, 3) void
+MOZ_CrashPrintf(int aLine, const char* aFormat, ...);
+#  define MOZ_CALL_CRASH_PRINTF(format, ...) \
+     MOZ_CrashPrintf(__LINE__, format, __VA_ARGS__)
+#else
+MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(3, 4) void
+MOZ_CrashPrintf(const char* aFilename, int aLine, const char* aFormat, ...);
+#  define MOZ_CALL_CRASH_PRINTF(format, ...) \
+     MOZ_CrashPrintf(__FILE__, __LINE__, format, __VA_ARGS__)
+#endif
+
+/*
+ * MOZ_CRASH_UNSAFE_PRINTF(format, arg1 [, args]) can be used when more
+ * information is desired than a string literal can supply. The caller provides
+ * a printf-style format string, which must be a string literal and between
+ * 1 and 4 additional arguments. A regular MOZ_CRASH() is preferred wherever
+ * possible, as passing arbitrary strings to printf from a potentially
+ * compromised process is not without risk.
+ */
+#define MOZ_CRASH_UNSAFE_PRINTF(format, ...) \
+   do { \
+     MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+     static_assert( \
+       MOZ_PASTE_PREFIX_AND_ARG_COUNT(, __VA_ARGS__) <= sPrintfMaxArgs, \
+       "Only up to 4 additional arguments are allowed!"); \
+     static_assert(sizeof(format) <= sPrintfCrashReasonSize, \
+       "The supplied format string is too long!"); \
+     MOZ_CALL_CRASH_PRINTF("" format, __VA_ARGS__); \
+   } while (0)
+
 MOZ_END_EXTERN_C
 
 /*
  * MOZ_ASSERT(expr [, explanation-string]) asserts that |expr| must be truthy in
  * debug builds.  If it is, execution continues.  Otherwise, an error message
  * including the expression and the explanation-string (if provided) is printed,
  * an attempt is made to invoke any existing debugger, and execution halts.
  * MOZ_ASSERT is fatal: no recovery is possible.  Do not assert a condition