Bug 1274873 - Part 2: Move signal handler set up to SandboxEarlyInit() r=jld
authorJulian Hector <julian.r.hector@gmail.com>
Thu, 26 May 2016 16:20:44 +0200
changeset 300891 17c1f2315eb528745cfc7b73b1f5dc6cfcf7a35c
parent 300890 1526b47e25e446cbbbaee357ed3d643fef45eb2a
child 300892 a29db603cf6ec486f6065b1e4471befda71fa6da
push id19599
push usercbook@mozilla.com
push dateWed, 08 Jun 2016 10:16:21 +0000
treeherderfx-team@81f4cc3f6f4c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjld
bugs1274873
milestone50.0a1
Bug 1274873 - Part 2: Move signal handler set up to SandboxEarlyInit() r=jld
security/sandbox/linux/Sandbox.cpp
security/sandbox/linux/SandboxHooks.cpp
--- a/security/sandbox/linux/Sandbox.cpp
+++ b/security/sandbox/linux/Sandbox.cpp
@@ -60,16 +60,19 @@ typedef struct {
   unsigned int coverage_max_block_size;
 } __sanitizer_sandbox_arguments;
 
 MOZ_IMPORT_API void
 __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
 } // extern "C"
 #endif // MOZ_ASAN
 
+// Signal number used to enable seccomp on each thread.
+int gSeccompTsyncBroadcastSignum = 0;
+
 namespace mozilla {
 
 #ifdef ANDROID
 SandboxCrashFunc gSandboxCrashFunc;
 #endif
 
 #ifdef MOZ_GMP_SANDBOX
 // For media plugins, we can start the sandbox before we dlopen the
@@ -302,17 +305,16 @@ EnterChroot()
     gChrootHelper->Invoke();
     gChrootHelper = nullptr;
   }
 }
 
 static void
 BroadcastSetThreadSandbox(const sock_fprog* aFilter)
 {
-  int signum;
   pid_t pid, tid, myTid;
   DIR *taskdp;
   struct dirent *de;
 
   // This function does not own *aFilter, so this global needs to
   // always be zeroed before returning.
   gSetSandboxFilter = aFilter;
 
@@ -323,29 +325,16 @@ BroadcastSetThreadSandbox(const sock_fpr
   taskdp = opendir("/proc/self/task");
   if (taskdp == nullptr) {
     SANDBOX_LOG_ERROR("opendir /proc/self/task: %s\n", strerror(errno));
     MOZ_CRASH();
   }
 
   EnterChroot();
 
-  signum = FindFreeSignalNumber();
-  if (signum == 0) {
-    SANDBOX_LOG_ERROR("No available signal numbers!");
-    MOZ_CRASH();
-  }
-  void (*oldHandler)(int);
-  oldHandler = signal(signum, SetThreadSandboxHandler);
-  if (oldHandler != SIG_DFL) {
-    // See the comment on FindFreeSignalNumber about race conditions.
-    SANDBOX_LOG_ERROR("signal %d in use by handler %p!\n", signum, oldHandler);
-    MOZ_CRASH();
-  }
-
   // In case this races with a not-yet-deprivileged thread cloning
   // itself, repeat iterating over all threads until we find none
   // that are still privileged.
   bool sandboxProgress;
   do {
     sandboxProgress = false;
     // For each thread...
     while ((de = readdir(taskdp))) {
@@ -355,19 +344,22 @@ BroadcastSetThreadSandbox(const sock_fpr
         // Not a task ID.
         continue;
       }
       if (tid == myTid) {
         // Drop this thread's privileges last, below, so we can
         // continue to signal other threads.
         continue;
       }
+
+      MOZ_RELEASE_ASSERT(gSeccompTsyncBroadcastSignum != 0);
+
       // Reset the futex cell and signal.
       gSetSandboxDone = 0;
-      if (syscall(__NR_tgkill, pid, tid, signum) != 0) {
+      if (syscall(__NR_tgkill, pid, tid, gSeccompTsyncBroadcastSignum) != 0) {
         if (errno == ESRCH) {
           SANDBOX_LOG_ERROR("Thread %d unexpectedly exited.", tid);
           // Rescan threads, in case it forked before exiting.
           sandboxProgress = true;
           continue;
         }
         SANDBOX_LOG_ERROR("tgkill(%d,%d): %s\n", pid, tid, strerror(errno));
         MOZ_CRASH();
@@ -427,21 +419,24 @@ BroadcastSetThreadSandbox(const sock_fpr
                             "  Killing process.",
                             tid, crashDelay);
           MOZ_CRASH();
         }
       }
     }
     rewinddir(taskdp);
   } while (sandboxProgress);
-  oldHandler = signal(signum, SIG_DFL);
+
+  void (*oldHandler)(int);
+  oldHandler = signal(gSeccompTsyncBroadcastSignum, SIG_DFL);
+  gSeccompTsyncBroadcastSignum = 0;
   if (oldHandler != SetThreadSandboxHandler) {
     // See the comment on FindFreeSignalNumber about race conditions.
     SANDBOX_LOG_ERROR("handler for signal %d was changed to %p!",
-                      signum, oldHandler);
+                      gSeccompTsyncBroadcastSignum, oldHandler);
     MOZ_CRASH();
   }
   Unused << closedir(taskdp);
   // And now, deprivilege the main thread:
   SetThreadSandbox();
   gSetSandboxFilter = nullptr;
 }
 
@@ -553,16 +548,35 @@ SandboxEarlyInit(GeckoProcessType aType,
 #endif
     // In the future, content processes will be able to use some of
     // these.
   default:
     // Other cases intentionally left blank.
     break;
   }
 
+  // If TSYNC is not supported, set up signal handler
+  // used to enable seccomp on each thread.
+  if (!info.Test(SandboxInfo::kHasSeccompTSync)) {
+    gSeccompTsyncBroadcastSignum = FindFreeSignalNumber();
+    if (gSeccompTsyncBroadcastSignum == 0) {
+      SANDBOX_LOG_ERROR("No available signal numbers!");
+      MOZ_CRASH();
+    }
+
+    void (*oldHandler)(int);
+    oldHandler = signal(gSeccompTsyncBroadcastSignum, SetThreadSandboxHandler);
+    if (oldHandler != SIG_DFL) {
+      // See the comment on FindFreeSignalNumber about race conditions.
+      SANDBOX_LOG_ERROR("signal %d in use by handler %p!\n",
+        gSeccompTsyncBroadcastSignum, oldHandler);
+      MOZ_CRASH();
+    }
+  }
+
   // If there's nothing to do, then we're done.
   if (!canChroot && !canUnshareNet && !canUnshareIPC) {
     return;
   }
 
   {
     LinuxCapabilities existingCaps;
     if (existingCaps.GetCurrent() && existingCaps.AnyEffective()) {
--- a/security/sandbox/linux/SandboxHooks.cpp
+++ b/security/sandbox/linux/SandboxHooks.cpp
@@ -7,16 +7,19 @@
 #include <signal.h>
 #include <errno.h>
 
 #include "mozilla/Types.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 
+// Signal number used to enable seccomp on each thread.
+extern int gSeccompTsyncBroadcastSignum;
+
 // This file defines a hook for sigprocmask() and pthread_sigmask().
 // Bug 1176099: some threads block SIGSYS signal which breaks our seccomp-bpf
 // sandbox. To avoid this, we intercept the call and remove SIGSYS.
 //
 // ENOSYS indicates an error within the hook function itself.
 static int HandleSigset(int (*aRealFunc)(int, const sigset_t*, sigset_t*),
                         int aHow, const sigset_t* aSet,
                         sigset_t* aOldSet, bool aUseErrno)
@@ -26,21 +29,24 @@ static int HandleSigset(int (*aRealFunc)
       errno = ENOSYS;
       return -1;
     }
 
     return ENOSYS;
   }
 
   // Avoid unnecessary work
-  if (aSet == NULL || aHow == SIG_UNBLOCK || !sigismember(aSet, SIGSYS))
+  if (aSet == NULL || aHow == SIG_UNBLOCK) {
     return aRealFunc(aHow, aSet, aOldSet);
+  }
 
   sigset_t newSet = *aSet;
-  if (sigdelset(&newSet, SIGSYS) != 0) {
+  if (sigdelset(&newSet, SIGSYS) != 0 ||
+     (gSeccompTsyncBroadcastSignum &&
+      sigdelset(&newSet, gSeccompTsyncBroadcastSignum) != 0)) {
     if (aUseErrno) {
       errno = ENOSYS;
       return -1;
     }
 
     return ENOSYS;
   }