Bug 1274540 - Record sandboxing status in crash reports; r?gcp draft
authorHaik Aftandilian <haftandilian@mozilla.com>
Wed, 20 Jul 2016 13:53:11 -0700
changeset 390245 7fcc8e6cc84c7a6a9653530f76607d84d334113b
parent 390029 d224fc999cb6accb208af0a105f14433375e2e77
child 525961 8d9c6d0b542eddb5262b56a6fd5feee9a234cf3f
push id23629
push userhaftandilian@mozilla.com
push dateWed, 20 Jul 2016 21:31:04 +0000
reviewersgcp
bugs1274540
milestone50.0a1
Bug 1274540 - Record sandboxing status in crash reports; r?gcp Adds content sandbox metadata to parent and child crash reports: Includes the value of pref security.sandbox.content.level, whether or not the system is capable of sandboxing, if the sandbox was successfully turned on, and (on Linux systems) the sandbox capabilities flags. New crash report keys: "ContentSandboxLevel" in parent and content "ContentSandboxCapable" in parent "ContentSandboxEnabled" in content "ContentSandboxCapabilities" in content on Linux MozReview-Commit-ID: 6n9u0s41yJr
dom/ipc/ContentChild.cpp
security/sandbox/linux/Sandbox.cpp
security/sandbox/linux/Sandbox.h
security/sandbox/linux/common/SandboxInfo.cpp
security/sandbox/linux/common/SandboxInfo.h
security/sandbox/mac/Sandbox.mm
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsAppRunner.h
toolkit/xre/nsEmbedFunctions.cpp
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1352,19 +1352,24 @@ GetAppPaths(nsCString &aAppPath, nsCStri
     appDir->GetNativeTarget(aAppDir);
   } else {
     appDir->GetNativePath(aAppDir);
   }
 
   return true;
 }
 
-static void
+static bool
 StartMacOSContentSandbox()
 {
+  int sandboxLevel = Preferences::GetInt("security.sandbox.content.level");
+  if (sandboxLevel < 1) {
+    return false;
+  }
+
   nsAutoCString appPath, appBinaryPath, appDir;
   if (!GetAppPaths(appPath, appBinaryPath, appDir)) {
     MOZ_CRASH("Error resolving child process path");
   }
 
   // During sandboxed content process startup, before reaching
   // this point, NS_OS_TEMP_DIR is modified to refer to a sandbox-
   // writable temporary directory
@@ -1378,69 +1383,84 @@ StartMacOSContentSandbox()
   nsAutoCString tempDirPath;
   rv = tempDir->GetNativePath(tempDirPath);
   if (NS_FAILED(rv)) {
     MOZ_CRASH("Failed to get NS_OS_TEMP_DIR path");
   }
 
   MacSandboxInfo info;
   info.type = MacSandboxType_Content;
-  info.level = Preferences::GetInt("security.sandbox.content.level");
+  info.level = sandboxLevel;
   info.appPath.assign(appPath.get());
   info.appBinaryPath.assign(appBinaryPath.get());
   info.appDir.assign(appDir.get());
   info.appTempDir.assign(tempDirPath.get());
 
   std::string err;
   if (!mozilla::StartMacSandbox(info, err)) {
     NS_WARNING(err.c_str());
     MOZ_CRASH("sandbox_init() failed");
   }
+
+  return true;
 }
 #endif
 
 bool
 ContentChild::RecvSetProcessSandbox(const MaybeFileDesc& aBroker)
 {
   // We may want to move the sandbox initialization somewhere else
   // at some point; see bug 880808.
 #if defined(MOZ_CONTENT_SANDBOX)
+  bool sandboxEnabled = true;
 #if defined(XP_LINUX)
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19
   // For B2G >= KitKat, sandboxing is mandatory; this has already
   // been enforced by ContentParent::StartUp().
   MOZ_ASSERT(SandboxInfo::Get().CanSandboxContent());
 #else
   // Otherwise, sandboxing is best-effort.
   if (!SandboxInfo::Get().CanSandboxContent()) {
-      return true;
+      sandboxEnabled = false;
+  } else {
+      // This triggers the initialization of cubeb, which needs to happen
+      // before seccomp is enabled (Bug 1259508). It also increases the startup
+      // time of the content process, because cubeb is usually initialized
+      // when it is actually needed. This call here is no longer required
+      // once Bug 1104619 (remoting audio) is resolved.
+      Unused << CubebUtils::GetCubebContext();
   }
-
-  // This triggers the initialization of cubeb, which needs to happen
-  // before seccomp is enabled (Bug 1259508). It also increases the startup
-  // time of the content process, because cubeb is usually initialized
-  // when it is actually needed. This call here is no longer required
-  // once Bug 1104619 (remoting audio) is resolved.
-  Unused << CubebUtils::GetCubebContext();
-#endif
-  int brokerFd = -1;
-  if (aBroker.type() == MaybeFileDesc::TFileDescriptor) {
-      brokerFd = aBroker.get_FileDescriptor().PlatformHandle();
-      // brokerFd < 0 means to allow direct filesystem access, so
-      // make absolutely sure that doesn't happen if the parent
-      // didn't intend it.
-      MOZ_RELEASE_ASSERT(brokerFd >= 0);
+#endif /* MOZ_WIDGET_GONK && ANDROID_VERSION >= 19 */
+  if (sandboxEnabled) {
+      int brokerFd = -1;
+      if (aBroker.type() == MaybeFileDesc::TFileDescriptor) {
+          brokerFd = aBroker.get_FileDescriptor().PlatformHandle();
+          // brokerFd < 0 means to allow direct filesystem access, so
+          // make absolutely sure that doesn't happen if the parent
+          // didn't intend it.
+          MOZ_RELEASE_ASSERT(brokerFd >= 0);
+      }
+      sandboxEnabled = SetContentProcessSandbox(brokerFd);
   }
-  SetContentProcessSandbox(brokerFd);
 #elif defined(XP_WIN)
   mozilla::SandboxTarget::Instance()->StartSandbox();
 #elif defined(XP_MACOSX)
-  StartMacOSContentSandbox();
+  sandboxEnabled = StartMacOSContentSandbox();
 #endif
-#endif
+
+#if defined(MOZ_CRASHREPORTER)
+  CrashReporter::AnnotateCrashReport(
+    NS_LITERAL_CSTRING("ContentSandboxEnabled"),
+    sandboxEnabled? NS_LITERAL_CSTRING("1") : NS_LITERAL_CSTRING("0"));
+#if defined(XP_LINUX) && !defined(OS_ANDROID)
+  SandboxInfo::Get().AnnotateCrashReport();
+#endif /* XP_LINUX && !OS_ANDROID */
+#endif /* MOZ_CRASHREPORTER */
+#endif /* MOZ_CONTENT_SANDBOX */
+
   return true;
 }
 
 bool
 ContentChild::RecvSpeakerManagerNotify()
 {
 #ifdef MOZ_WIDGET_GONK
   // Only notify the process which has the SpeakerManager instance.
--- a/security/sandbox/linux/Sandbox.cpp
+++ b/security/sandbox/linux/Sandbox.cpp
@@ -638,33 +638,34 @@ SandboxEarlyInit(GeckoProcessType aType,
 
 #ifdef MOZ_CONTENT_SANDBOX
 /**
  * Starts the seccomp sandbox for a content process.  Should be called
  * only once, and before any potentially harmful content is loaded.
  *
  * Will normally make the process exit on failure.
 */
-void
+bool
 SetContentProcessSandbox(int aBrokerFd)
 {
   if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForContent)) {
     if (aBrokerFd >= 0) {
       close(aBrokerFd);
     }
-    return;
+    return false;
   }
 
   // This needs to live until the process exits.
   static Maybe<SandboxBrokerClient> sBroker;
   if (aBrokerFd >= 0) {
     sBroker.emplace(aBrokerFd);
   }
 
   SetCurrentProcessSandbox(GetContentSandboxPolicy(sBroker.ptrOr(nullptr)));
+  return true;
 }
 #endif // MOZ_CONTENT_SANDBOX
 
 #ifdef MOZ_GMP_SANDBOX
 /**
  * Starts the seccomp sandbox for a media plugin process.  Should be
  * called only once, and before any potentially harmful content is
  * loaded -- including the plugin itself, if it's considered untrusted.
--- a/security/sandbox/linux/Sandbox.h
+++ b/security/sandbox/linux/Sandbox.h
@@ -27,17 +27,17 @@ namespace mozilla {
 // This must be called early, while the process is still single-threaded.
 MOZ_SANDBOX_EXPORT void SandboxEarlyInit(GeckoProcessType aType, bool aIsNuwa);
 
 #ifdef MOZ_CONTENT_SANDBOX
 // Call only if SandboxInfo::CanSandboxContent() returns true.
 // (No-op if MOZ_DISABLE_CONTENT_SANDBOX is set.)
 // aBrokerFd is the filesystem broker client file descriptor,
 // or -1 to allow direct filesystem access.
-MOZ_SANDBOX_EXPORT void SetContentProcessSandbox(int aBrokerFd);
+MOZ_SANDBOX_EXPORT bool SetContentProcessSandbox(int aBrokerFd);
 #endif
 
 #ifdef MOZ_GMP_SANDBOX
 // Call only if SandboxInfo::CanSandboxMedia() returns true.
 // (No-op if MOZ_DISABLE_GMP_SANDBOX is set.)
 // aFilePath is the path to the plugin file.
 MOZ_SANDBOX_EXPORT void SetMediaPluginSandbox(const char *aFilePath);
 #endif
--- a/security/sandbox/linux/common/SandboxInfo.cpp
+++ b/security/sandbox/linux/common/SandboxInfo.cpp
@@ -18,16 +18,25 @@
 
 #include "base/posix/eintr_wrapper.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Telemetry.h"
 #include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
 #include "sandbox/linux/services/linux_syscalls.h"
 
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#include "nsICrashReporter.h"
+#define NS_CRASHREPORTER_CONTRACTID "@mozilla.org/toolkit/crash-reporter;1"
+#include "nsIPrefService.h"
+#include "nsIMemoryInfoDumper.h"
+#endif
+
+
 // A note about assertions: in general, the worst thing this module
 // should be able to do is disable sandboxing features, so release
 // asserts or MOZ_CRASH should be avoided, even for seeming
 // impossibilities like an unimplemented syscall returning success
 // (which has happened: https://crbug.com/439795 ).
 //
 // MOZ_DIAGNOSTIC_ASSERT (debug builds, plus Nightly/Aurora non-debug)
 // is probably the best choice for conditions that shouldn't be able
@@ -278,9 +287,19 @@ SandboxInfo::SubmitTelemetry()
       Telemetry::SANDBOX_CAPABILITIES_ENABLED_CONTENT, true);
   }
   if (sandboxInfo.Test(SandboxInfo::kEnabledForMedia)) {
     Telemetry::Accumulate(
       Telemetry::SANDBOX_CAPABILITIES_ENABLED_MEDIA, true);
   }
 }
 
+void
+SandboxInfo::AnnotateCrashReport() const
+{
+  nsAutoCString flagsString;
+  flagsString.AppendInt(mFlags);
+
+  CrashReporter::AnnotateCrashReport(
+    NS_LITERAL_CSTRING("ContentSandboxCapabilities"), flagsString);
+}
+
 } // namespace mozilla
--- a/security/sandbox/linux/common/SandboxInfo.h
+++ b/security/sandbox/linux/common/SandboxInfo.h
@@ -52,16 +52,18 @@ public:
   }
 
   // Returns true if SetMediaPluginSandbox may be called.
   bool CanSandboxMedia() const
   {
     return !Test(kEnabledForMedia) || Test(kHasSeccompBPF);
   }
 
+  MOZ_EXPORT void AnnotateCrashReport() const;
+
   static void SubmitTelemetry();
 
   // For bug 1222500 or anything else like it: On desktop, this is
   // called in the parent process at a point when it should still be
   // single-threaded, to check that the SandboxEarlyInit() call in a
   // child process is early enough to be single-threaded.  If not,
   // kUnexpectedThreads is set and affected flags (user namespaces;
   // possibly others in the future) are cleared.
--- a/security/sandbox/mac/Sandbox.mm
+++ b/security/sandbox/mac/Sandbox.mm
@@ -10,16 +10,18 @@
 // linking to nsCocoaFeatures.mm in XUL.
 
 #include "Sandbox.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <CoreFoundation/CoreFoundation.h>
 
+#include "mozilla/Assertions.h"
+
 // XXX There are currently problems with the /usr/include/sandbox.h file on
 // some/all of the Macs in Mozilla's build system.  For the time being (until
 // this problem is resolved), we refer directly to what we need from it,
 // rather than including it here.
 extern "C" int sandbox_init(const char *profile, uint64_t flags, char **errorbuf);
 extern "C" void sandbox_free_error(char *errorbuf);
 
 #define MAC_OS_X_VERSION_10_0_HEX  0x00001000
@@ -485,28 +487,29 @@ bool StartMacSandbox(MacSandboxInfo aInf
       char *widevineProfile = NULL;
       asprintf(&widevineProfile, "%s%s", profile,
         widevinePluginSandboxRulesAddend);
       free(profile);
       profile = widevineProfile;
     }
   }
   else if (aInfo.type == MacSandboxType_Content) {
+    MOZ_ASSERT(aInfo.level >= 1);
     if (aInfo.level >= 1) {
       asprintf(&profile, contentSandboxRules, aInfo.level,
                OSXVersion::OSXVersionMinor(),
                aInfo.appPath.c_str(),
                aInfo.appBinaryPath.c_str(),
                aInfo.appDir.c_str(),
                aInfo.appTempDir.c_str(),
                getenv("HOME"));
     } else {
       fprintf(stderr,
         "Content sandbox disabled due to sandbox level setting\n");
-      return (true);
+      return false;
     }
   }
   else {
     char *msg = NULL;
     asprintf(&msg, "Unexpected sandbox type %u", aInfo.type);
     if (msg) {
       aErrorMessage.assign(msg);
       free(msg);
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -3904,16 +3904,49 @@ XREMain::XRE_mainStartup(bool* aExitFlag
 
   if (flagFile) {
     flagFile->Remove(true);
   }
 
   return 0;
 }
 
+#if defined(MOZ_CRASHREPORTER)
+#if defined(MOZ_CONTENT_SANDBOX) && !defined(MOZ_WIDGET_GONK)
+void AddSandboxAnnotations()
+{
+  // Include the sandbox content level, regardless of platform
+  int level = Preferences::GetInt("security.sandbox.content.level");
+
+  nsAutoCString levelString;
+  levelString.AppendInt(level);
+
+  CrashReporter::AnnotateCrashReport(
+    NS_LITERAL_CSTRING("ContentSandboxLevel"), levelString);
+
+  // Include whether or not this instance is capable of content sandboxing
+  bool sandboxCapable = false;
+
+#if defined(XP_WIN)
+  // All supported Windows versions support some level of content sandboxing
+  sandboxCapable = true;
+#elif defined(XP_MACOSX)
+  // All supported OS X versions are capable
+  sandboxCapable = true;
+#elif defined(XP_LINUX)
+  sandboxCapable = SandboxInfo::Get().CanSandboxContent();
+#endif
+
+  CrashReporter::AnnotateCrashReport(
+    NS_LITERAL_CSTRING("ContentSandboxCapable"),
+    sandboxCapable ? NS_LITERAL_CSTRING("1") : NS_LITERAL_CSTRING("0"));
+}
+#endif /* MOZ_CONTENT_SANDBOX && !MOZ_WIDGET_GONK */
+#endif /* MOZ_CRASHREPORTER */
+
 /*
  * XRE_mainRun - Command line startup, profile migration, and
  * the calling of appStartup->Run().
  */
 nsresult
 XREMain::XRE_mainRun()
 {
   nsresult rv = NS_OK;
@@ -4198,21 +4231,30 @@ XREMain::XRE_mainRun()
 
 #ifdef MOZ_INSTRUMENT_EVENT_LOOP
   if (PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP")) {
     bool logToConsole = true;
     mozilla::InitEventTracing(logToConsole);
   }
 #endif /* MOZ_INSTRUMENT_EVENT_LOOP */
 
-#if defined(MOZ_SANDBOX) && defined(XP_LINUX) && !defined(ANDROID)
+#if defined(MOZ_SANDBOX) && defined(XP_LINUX) && !defined(MOZ_WIDGET_GONK)
   // If we're on Linux, we now have information about the OS capabilities
   // available to us.
   SandboxInfo::SubmitTelemetry();
-#endif
+#if defined(MOZ_CRASHREPORTER)
+  SandboxInfo::Get().AnnotateCrashReport();
+#endif /* MOZ_CRASHREPORTER */
+#endif /* MOZ_SANDBOX && XP_LINUX && !MOZ_WIDGET_GONK */
+
+#if defined(MOZ_CRASHREPORTER)
+#if defined(MOZ_CONTENT_SANDBOX) && !defined(MOZ_WIDGET_GONK)
+  AddSandboxAnnotations();
+#endif /* MOZ_CONTENT_SANDBOX && !MOZ_WIDGET_GONK */
+#endif /* MOZ_CRASHREPORTER */
 
   {
     rv = appStartup->Run();
     if (NS_FAILED(rv)) {
       NS_ERROR("failed to run appstartup");
       gLogConsoleErrors = true;
     }
   }
--- a/toolkit/xre/nsAppRunner.h
+++ b/toolkit/xre/nsAppRunner.h
@@ -19,17 +19,16 @@
 #define MAXPATHLEN _MAX_PATH
 #elif defined(CCHMAXPATH)
 #define MAXPATHLEN CCHMAXPATH
 #else
 #define MAXPATHLEN 1024
 #endif
 #endif
 
-#include "nscore.h"
 #include "nsXULAppAPI.h"
 
 // This directory service key is a lot like NS_APP_LOCALSTORE_50_FILE,
 // but it is always the "main" localstore file, even when we're in safe mode
 // and we load localstore from somewhere else.
 #define NS_LOCALSTORE_UNSAFE_FILE "LStoreS"
 
 class nsINativeAppSupport;
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -74,16 +74,20 @@
 
 #include "mozilla/Telemetry.h"
 
 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
 #include "mozilla/sandboxTarget.h"
 #include "mozilla/sandboxing/loggingCallbacks.h"
 #endif
 
+#if defined(MOZ_CONTENT_SANDBOX) && !defined(MOZ_WIDGET_GONK)
+#include "mozilla/Preferences.h"
+#endif
+
 #ifdef MOZ_IPDL_TESTS
 #include "mozilla/_ipdltest/IPDLUnitTests.h"
 #include "mozilla/_ipdltest/IPDLUnitTestProcessChild.h"
 
 using mozilla::_ipdltest::IPDLUnitTestProcessChild;
 #endif  // ifdef MOZ_IPDL_TESTS
 
 #ifdef MOZ_B2G_LOADER
@@ -289,16 +293,32 @@ SetTaskbarGroupId(const nsString& aId)
         NS_WARNING("SetCurrentProcessExplicitAppUserModelID failed for child process.");
     }
 
     if (hDLL)
         ::FreeLibrary(hDLL);
 }
 #endif
 
+#if defined(MOZ_CRASHREPORTER)
+#if defined(MOZ_CONTENT_SANDBOX) && !defined(MOZ_WIDGET_GONK)
+void
+AddContentSandboxLevelAnnotation()
+{
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    int level = Preferences::GetInt("security.sandbox.content.level");
+    nsAutoCString levelString;
+    levelString.AppendInt(level);
+    CrashReporter::AnnotateCrashReport(
+      NS_LITERAL_CSTRING("ContentSandboxLevel"), levelString);
+  }
+}
+#endif /* MOZ_CONTENT_SANDBOX && !MOZ_WIDGET_GONK */
+#endif /* MOZ_CRASHREPORTER */
+
 nsresult
 XRE_InitChildProcess(int aArgc,
                      char* aArgv[],
                      const XREChildData* aChildData)
 {
   NS_ENSURE_ARG_MIN(aArgc, 2);
   NS_ENSURE_ARG_POINTER(aArgv);
   NS_ENSURE_ARG_POINTER(aArgv[0]);
@@ -650,16 +670,22 @@ XRE_InitChildProcess(int aArgc,
 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
       // We need to do this after the process has been initialised, as
       // InitLoggingIfRequired may need access to prefs.
       mozilla::sandboxing::InitLoggingIfRequired(aChildData->ProvideLogFunction);
 #endif
 
       OverrideDefaultLocaleIfNeeded();
 
+#if defined(MOZ_CRASHREPORTER)
+#if defined(MOZ_CONTENT_SANDBOX) && !defined(MOZ_WIDGET_GONK)
+      AddContentSandboxLevelAnnotation();
+#endif
+#endif
+
       // Run the UI event loop on the main thread.
       uiMessageLoop.MessageLoop::Run();
 
       // Allow ProcessChild to clean up after itself before going out of
       // scope and being deleted
       process->CleanUp();
       mozilla::Omnijar::CleanUp();