Bug 1041886 - Separate Linux sandbox code into its own shared library. r=kang r=glandium
authorJed Davis <jld@mozilla.com>
Tue, 26 Aug 2014 13:54:09 -0700
changeset 201711 2f9d0821e08cdf73a7c6e32e9bc2ecf440960197
parent 201710 b3dcb5b33f780a0266be0c9261e297fe43e3b1a8
child 201712 ee14fb2a1053fff9b4a5cb5f0dd0486ddbe3ceb1
push id48242
push userjedavis@mozilla.com
push dateTue, 26 Aug 2014 20:54:43 +0000
treeherdermozilla-inbound@ee14fb2a1053 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskang, glandium
bugs1041886
milestone34.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 1041886 - Separate Linux sandbox code into its own shared library. r=kang r=glandium This creates libmozsandbox.so on builds that use sandboxing (MOZ_CONTENT_SANDBOX or MOZ_GMP_SANDBOX). The unavoidably libxul-dependent parts, for invoking the crash reporter and printing the JS context, are separated into glue/SandboxCrash.cpp and invoked via a callback.
b2g/installer/package-manifest.in
browser/installer/package-manifest.in
mobile/android/installer/package-manifest.in
security/sandbox/linux/Sandbox.cpp
security/sandbox/linux/Sandbox.h
security/sandbox/linux/SandboxInternal.h
security/sandbox/linux/glue/SandboxCrash.cpp
security/sandbox/linux/glue/moz.build
security/sandbox/linux/moz.build
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -739,16 +739,20 @@
 #endif
 #endif
 @BINPATH@/chrome/pippki@JAREXT@
 @BINPATH@/chrome/pippki.manifest
 
 ; For process sandboxing
 #if defined(XP_WIN)
 @BINPATH@/@DLL_PREFIX@sandboxbroker@DLL_SUFFIX@
+#elif defined(XP_LINUX)
+#if defined(MOZ_CONTENT_SANDBOX) || defined(MOZ_GMP_SANDBOX)
+@BINPATH@/@DLL_PREFIX@mozsandbox@DLL_SUFFIX@
+#endif
 #endif
 
 ; for Solaris SPARC
 #ifdef SOLARIS
 bin/libfreebl_32fpu_3.chk
 bin/libfreebl_32fpu_3.so
 bin/libfreebl_32int_3.chk
 bin/libfreebl_32int_3.so
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -749,16 +749,20 @@
 @BINPATH@/chrome/pippki.manifest
 @BINPATH@/components/pipboot.xpt
 @BINPATH@/components/pipnss.xpt
 @BINPATH@/components/pippki.xpt
 
 ; For process sandboxing
 #if defined(XP_WIN)
 @BINPATH@/@DLL_PREFIX@sandboxbroker@DLL_SUFFIX@
+#elif defined(XP_LINUX)
+#if defined(MOZ_CONTENT_SANDBOX) || defined(MOZ_GMP_SANDBOX)
+@BINPATH@/@DLL_PREFIX@mozsandbox@DLL_SUFFIX@
+#endif
 #endif
 
 ; for Solaris SPARC
 #ifdef SOLARIS
 bin/libfreebl_32fpu_3.so
 bin/libfreebl_32int_3.so
 bin/libfreebl_32int64_3.so
 #endif
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -539,16 +539,21 @@
 ; [Personal Security Manager]
 ;
 @BINPATH@/components/pipboot.xpt
 @BINPATH@/components/pipnss.xpt
 @BINPATH@/components/pippki.xpt
 @BINPATH@/chrome/pippki@JAREXT@
 @BINPATH@/chrome/pippki.manifest
 
+; For process sandboxing
+#if defined(MOZ_CONTENT_SANDBOX) || defined(MOZ_GMP_SANDBOX)
+@BINPATH@/@DLL_PREFIX@mozsandbox@DLL_SUFFIX@
+#endif
+
 ; for Solaris SPARC
 #ifdef SOLARIS
 bin/libfreebl_32fpu_3.chk
 bin/libfreebl_32fpu_3.so
 bin/libfreebl_32int_3.chk
 bin/libfreebl_32int_3.so
 bin/libfreebl_32int64_3.chk
 bin/libfreebl_32int64_3.so
--- a/security/sandbox/linux/Sandbox.cpp
+++ b/security/sandbox/linux/Sandbox.cpp
@@ -1,15 +1,16 @@
 /* -*- 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 "Sandbox.h"
+#include "SandboxInternal.h"
 #include "SandboxLogging.h"
 
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/ptrace.h>
 #include <sys/prctl.h>
 #include <sys/syscall.h>
 #include <signal.h>
@@ -20,36 +21,31 @@
 #include <stdlib.h>
 #include <pthread.h>
 #include <errno.h>
 #include <fcntl.h>
 
 #include "mozilla/Atomics.h"
 #include "mozilla/NullPtr.h"
 #include "mozilla/unused.h"
-#include "mozilla/dom/Exceptions.h"
-#include "nsThreadUtils.h"
-#include "prenv.h"
-
-#ifdef MOZ_CRASHREPORTER
-#include "nsExceptionHandler.h"
-#endif
 
 #if defined(ANDROID)
 #include "android_ucontext.h"
 #endif
 
 #include "linux_seccomp.h"
 #include "SandboxFilter.h"
 
 // See definition of SandboxDie, below.
 #include "sandbox/linux/seccomp-bpf/die.h"
 
 namespace mozilla {
 
+SandboxCrashFunc gSandboxCrashFunc;
+
 #ifdef MOZ_GMP_SANDBOX
 // For media plugins, we can start the sandbox before we dlopen the
 // module, so we have to pre-open the file and simulate the sandboxed
 // open().
 static int gMediaPluginFileDesc = -1;
 static const char *gMediaPluginFilePath;
 #endif
 
@@ -84,73 +80,30 @@ struct SandboxFlags {
     isDisabledForGMP = getenv("MOZ_DISABLE_GMP_SANDBOX");
 #endif
   }
 };
 
 static const SandboxFlags gSandboxFlags;
 
 /**
- * Log JS stack info in the same place as the sandbox violation
- * message.  Useful in case the responsible code is JS and all we have
- * are logs and a minidump with the C++ stacks (e.g., on TBPL).
- */
-static void
-SandboxLogJSStack(void)
-{
-  if (!NS_IsMainThread()) {
-    // This might be a worker thread... or it might be a non-JS
-    // thread, or a non-NSPR thread.  There's isn't a good API for
-    // dealing with this, yet.
-    return;
-  }
-  nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack();
-  for (int i = 0; frame != nullptr; ++i) {
-    nsAutoString fileName, funName;
-    int32_t lineNumber;
-
-    // Don't stop unwinding if an attribute can't be read.
-    fileName.SetIsVoid(true);
-    unused << frame->GetFilename(fileName);
-    lineNumber = 0;
-    unused << frame->GetLineNumber(&lineNumber);
-    funName.SetIsVoid(true);
-    unused << frame->GetName(funName);
-
-    if (!funName.IsVoid() || !fileName.IsVoid()) {
-      SANDBOX_LOG_ERROR("JS frame %d: %s %s line %d", i,
-                        funName.IsVoid() ?
-                        "(anonymous)" : NS_ConvertUTF16toUTF8(funName).get(),
-                        fileName.IsVoid() ?
-                        "(no file)" : NS_ConvertUTF16toUTF8(fileName).get(),
-                        lineNumber);
-    }
-
-    nsCOMPtr<nsIStackFrame> nextFrame;
-    nsresult rv = frame->GetCaller(getter_AddRefs(nextFrame));
-    NS_ENSURE_SUCCESS_VOID(rv);
-    frame = nextFrame;
-  }
-}
-
-/**
  * This is the SIGSYS handler function. It is used to report to the user
  * which system call has been denied by Seccomp.
  * This function also makes the process exit as denying the system call
  * will otherwise generally lead to unexpected behavior from the process,
  * since we don't know if all functions will handle such denials gracefully.
  *
  * @see InstallSyscallReporter() function.
  */
 static void
 Reporter(int nr, siginfo_t *info, void *void_context)
 {
   ucontext_t *ctx = static_cast<ucontext_t*>(void_context);
   unsigned long syscall_nr, args[6];
-  pid_t pid = getpid(), tid = syscall(__NR_gettid);
+  pid_t pid = getpid();
 
   if (nr != SIGSYS) {
     return;
   }
   if (info->si_code != SYS_SECCOMP) {
     return;
   }
   if (!ctx) {
@@ -186,33 +139,20 @@ Reporter(int nr, siginfo_t *info, void *
   }
 #endif
 
   SANDBOX_LOG_ERROR("seccomp sandbox violation: pid %d, syscall %lu,"
                     " args %lu %lu %lu %lu %lu %lu.  Killing process.",
                     pid, syscall_nr,
                     args[0], args[1], args[2], args[3], args[4], args[5]);
 
-#ifdef MOZ_CRASHREPORTER
   // Bug 1017393: record syscall number somewhere useful.
   info->si_addr = reinterpret_cast<void*>(syscall_nr);
-  bool dumped = CrashReporter::WriteMinidumpForSigInfo(nr, info, void_context);
-  if (!dumped) {
-    SANDBOX_LOG_ERROR("Failed to write minidump");
-  }
-#endif
 
-  // Do this last, in case it crashes or deadlocks.
-  SandboxLogJSStack();
-
-  // Try to reraise, so the parent sees that this process crashed.
-  // (If tgkill is forbidden, then seccomp will raise SIGSYS, which
-  // also accomplishes that goal.)
-  signal(SIGSYS, SIG_DFL);
-  syscall(__NR_tgkill, pid, tid, nr);
+  gSandboxCrashFunc(nr, info, void_context);
   _exit(127);
 }
 
 /**
  * The reporter is called when the process receives a SIGSYS signal.
  * The signal is sent by the kernel when Seccomp encounter a system call
  * that has not been allowed.
  * We register an action for that signal (calling the Reporter function).
@@ -333,17 +273,17 @@ SetThreadSandboxHandler(int signum)
 static void
 BroadcastSetThreadSandbox(SandboxType aType)
 {
   int signum;
   pid_t pid, tid, myTid;
   DIR *taskdp;
   struct dirent *de;
   SandboxFilter filter(&sSetSandboxFilter, aType,
-                       PR_GetEnv("MOZ_SANDBOX_VERBOSE"));
+                       getenv("MOZ_SANDBOX_VERBOSE"));
 
   static_assert(sizeof(mozilla::Atomic<int>) == sizeof(int),
                 "mozilla::Atomic<int> isn't represented by an int");
   pid = getpid();
   myTid = syscall(__NR_gettid);
   taskdp = opendir("/proc/self/task");
   if (taskdp == nullptr) {
     SANDBOX_LOG_ERROR("opendir /proc/self/task: %s\n", strerror(errno));
@@ -464,16 +404,18 @@ BroadcastSetThreadSandbox(SandboxType aT
   // And now, deprivilege the main thread:
   SetThreadSandbox();
 }
 
 // Common code for sandbox startup.
 static void
 SetCurrentProcessSandbox(SandboxType aType)
 {
+  MOZ_ASSERT(gSandboxCrashFunc);
+
   if (InstallSyscallReporter()) {
     SANDBOX_LOG_ERROR("install_syscall_reporter() failed\n");
   }
 
   BroadcastSetThreadSandbox(aType);
 }
 
 #ifdef MOZ_CONTENT_SANDBOX
--- a/security/sandbox/linux/Sandbox.h
+++ b/security/sandbox/linux/Sandbox.h
@@ -2,32 +2,34 @@
 /* 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/. */
 
 #ifndef mozilla_Sandbox_h
 #define mozilla_Sandbox_h
 
+#include "mozilla/Types.h"
+
 namespace mozilla {
 
 // The Set*Sandbox() functions must not be called if the corresponding
 // CanSandbox*() function has returned false; if sandboxing is
 // attempted, any failure to enable it is fatal.
 //
 // If sandboxing is disabled for a process type with the corresponding
 // environment variable, Set*Sandbox() does nothing and CanSandbox*()
 // returns true.
 
 #ifdef MOZ_CONTENT_SANDBOX
 // Disabled by setting env var MOZ_DISABLE_CONTENT_SANDBOX.
-bool CanSandboxContentProcess();
-void SetContentProcessSandbox();
+MFBT_API bool CanSandboxContentProcess();
+MFBT_API void SetContentProcessSandbox();
 #endif
 #ifdef MOZ_GMP_SANDBOX
 // Disabled by setting env var MOZ_DISABLE_GMP_SANDBOX.
-bool CanSandboxMediaPlugin();
-void SetMediaPluginSandbox(const char *aFilePath);
+MFBT_API bool CanSandboxMediaPlugin();
+MFBT_API void SetMediaPluginSandbox(const char *aFilePath);
 #endif
 
 } // namespace mozilla
 
 #endif // mozilla_Sandbox_h
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/SandboxInternal.h
@@ -0,0 +1,24 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxInternal_h
+#define mozilla_SandboxInternal_h
+
+// The code in Sandbox.cpp can't link against libxul, where
+// SandboxCrash.cpp lives, so it has to use a callback, defined here.
+
+#include <signal.h>
+
+#include "mozilla/Types.h"
+
+namespace mozilla {
+
+typedef void (*SandboxCrashFunc)(int, siginfo_t*, void*);
+extern MFBT_API SandboxCrashFunc gSandboxCrashFunc;
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxInternal_h
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/glue/SandboxCrash.cpp
@@ -0,0 +1,99 @@
+/* -*- 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/. */
+
+// This file needs to be linked into libxul, so it can access the JS
+// stack and the crash reporter.  Everything else in this directory
+// should be able to be linked into its own shared library, in order
+// to be able to isolate sandbox/chromium from ipc/chromium.
+
+#include "SandboxInternal.h"
+#include "SandboxLogging.h"
+
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#include "mozilla/NullPtr.h"
+#include "mozilla/unused.h"
+#include "mozilla/dom/Exceptions.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
+namespace mozilla {
+
+// Log JS stack info in the same place as the sandbox violation
+// message.  Useful in case the responsible code is JS and all we have
+// are logs and a minidump with the C++ stacks (e.g., on TBPL).
+static void
+SandboxLogJSStack(void)
+{
+  if (!NS_IsMainThread()) {
+    // This might be a worker thread... or it might be a non-JS
+    // thread, or a non-NSPR thread.  There's isn't a good API for
+    // dealing with this, yet.
+    return;
+  }
+  nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack();
+  for (int i = 0; frame != nullptr; ++i) {
+    nsAutoString fileName, funName;
+    int32_t lineNumber;
+
+    // Don't stop unwinding if an attribute can't be read.
+    fileName.SetIsVoid(true);
+    unused << frame->GetFilename(fileName);
+    lineNumber = 0;
+    unused << frame->GetLineNumber(&lineNumber);
+    funName.SetIsVoid(true);
+    unused << frame->GetName(funName);
+
+    if (!funName.IsVoid() || !fileName.IsVoid()) {
+      SANDBOX_LOG_ERROR("JS frame %d: %s %s line %d", i,
+                        funName.IsVoid() ?
+                        "(anonymous)" : NS_ConvertUTF16toUTF8(funName).get(),
+                        fileName.IsVoid() ?
+                        "(no file)" : NS_ConvertUTF16toUTF8(fileName).get(),
+                        lineNumber);
+    }
+
+    nsCOMPtr<nsIStackFrame> nextFrame;
+    nsresult rv = frame->GetCaller(getter_AddRefs(nextFrame));
+    NS_ENSURE_SUCCESS_VOID(rv);
+    frame = nextFrame;
+  }
+}
+
+static void
+SandboxCrash(int nr, siginfo_t *info, void *void_context)
+{
+  pid_t pid = getpid(), tid = syscall(__NR_gettid);
+
+#ifdef MOZ_CRASHREPORTER
+  bool dumped = CrashReporter::WriteMinidumpForSigInfo(nr, info, void_context);
+  if (!dumped) {
+    SANDBOX_LOG_ERROR("Failed to write minidump");
+  }
+#endif
+
+  // Do this last, in case it crashes or deadlocks.
+  SandboxLogJSStack();
+
+  // Try to reraise, so the parent sees that this process crashed.
+  // (If tgkill is forbidden, then seccomp will raise SIGSYS, which
+  // also accomplishes that goal.)
+  signal(SIGSYS, SIG_DFL);
+  syscall(__NR_tgkill, pid, tid, nr);
+}
+
+static void __attribute__((constructor))
+SandboxSetCrashFunc()
+{
+  gSandboxCrashFunc = SandboxCrash;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/glue/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; python-indent: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+FAIL_ON_WARNINGS = True
+
+SOURCES += [
+    'SandboxCrash.cpp',
+]
+
+LOCAL_INCLUDES += [
+    '/security/sandbox/linux',
+]
+
+USE_LIBS += [
+    'mozsandbox',
+]
+
+FINAL_LIBRARY = 'xul'
--- a/security/sandbox/linux/moz.build
+++ b/security/sandbox/linux/moz.build
@@ -1,25 +1,40 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 FAIL_ON_WARNINGS = True
 
+LIBRARY_NAME = 'mozsandbox'
+FORCE_SHARED_LIB = True
+
 EXPORTS.mozilla += [
     'Sandbox.h',
 ]
 
 SOURCES += [
     '../chromium/sandbox/linux/seccomp-bpf/basicblock.cc',
     '../chromium/sandbox/linux/seccomp-bpf/codegen.cc',
     'Sandbox.cpp',
     'SandboxAssembler.cpp',
     'SandboxFilter.cpp',
 ]
 
-LOCAL_INCLUDES += ['/security/sandbox/chromium']
+DEFINES['NS_NO_XPCOM'] = True
+DISABLE_STL_WRAPPING = True
+
+LOCAL_INCLUDES += [
+    '/security/sandbox',
+    '/security/sandbox/chromium',
+]
 
-include('/ipc/chromium/chromium-config.mozbuild')
+if CONFIG['OS_TARGET'] != 'Android':
+    # Needed for clock_gettime with glibc < 2.17:
+    OS_LIBS += [
+        'rt',
+    ]
 
-FINAL_LIBRARY = 'xul'
+DIRS += [
+    'glue',
+]