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 373187 7fdb44e79e51279ac2076040f6bd7bc341b97c23
parent 373186 00d0810a4e6bc524973c12ac393e9a029ab80db2
child 373188 253f65ad681fdc0ebc8894b4ba6512598adeabbb
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1338574
milestone54.0a1
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