Bug 1257361 - Simplify detecting threads that already have seccomp-bpf applied. r=tedd r=gcp
authorJed Davis <jld@mozilla.com>
Tue, 06 Dec 2016 12:38:22 -1000
changeset 325283 26025c7f0d298872c5fe88739c9897254da6fc75
parent 325282 dbdc4e234e1f129f8a475a8709fa5e672fc93a34
child 325284 d13d437a39a14fa81221dfac4556c9ef347f5fa3
push id31048
push usercbook@mozilla.com
push dateWed, 07 Dec 2016 10:30:55 +0000
treeherdermozilla-central@c401d7293364 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstedd, gcp
bugs1257361
milestone53.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 1257361 - Simplify detecting threads that already have seccomp-bpf applied. r=tedd r=gcp
security/sandbox/linux/Sandbox.cpp
security/sandbox/linux/SandboxFilter.cpp
security/sandbox/linux/SandboxInternal.h
--- a/security/sandbox/linux/Sandbox.cpp
+++ b/security/sandbox/linux/Sandbox.cpp
@@ -207,51 +207,56 @@ InstallSigSysHandler(void)
  * whitelist).
  *
  * PR_SET_NO_NEW_PRIVS ensures that it is impossible to grant more
  * syscalls to the process beyond this point (even after fork()), and
  * prevents gaining capabilities (e.g., by exec'ing a setuid root
  * program).  The kernel won't allow seccomp-bpf without doing this,
  * because otherwise it could be used for privilege escalation attacks.
  *
- * Returns false (and sets errno) on failure.
+ * Returns false if the filter was already installed (see the
+ * PR_SET_NO_NEW_PRIVS rule in SandboxFilter.cpp).  Crashes on any
+ * other error condition.
  *
  * @see SandboxInfo
  * @see BroadcastSetThreadSandbox
  */
 static bool MOZ_MUST_USE
 InstallSyscallFilter(const sock_fprog *aProg, bool aUseTSync)
 {
   if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+    if (!aUseTSync && errno == ETXTBSY) {
+      return false;
+    }
     SANDBOX_LOG_ERROR("prctl(PR_SET_NO_NEW_PRIVS) failed: %s", strerror(errno));
     MOZ_CRASH("prctl(PR_SET_NO_NEW_PRIVS)");
   }
 
   if (aUseTSync) {
     if (syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
                 SECCOMP_FILTER_FLAG_TSYNC, aProg) != 0) {
       SANDBOX_LOG_ERROR("thread-synchronized seccomp failed: %s",
                         strerror(errno));
-      return false;
+      MOZ_CRASH("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER)");
     }
   } else {
     if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, (unsigned long)aProg, 0, 0)) {
       SANDBOX_LOG_ERROR("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER) failed: %s",
                         strerror(errno));
-      return false;
+      MOZ_CRASH("seccomp+tsync failed, but kernel supports tsync");
     }
   }
   return true;
 }
 
 // Use signals for permissions that need to be set per-thread.
 // The communication channel from the signal handler back to the main thread.
 static mozilla::Atomic<int> gSetSandboxDone;
 // Pass the filter itself through a global.
-static const sock_fprog* gSetSandboxFilter;
+const sock_fprog* gSetSandboxFilter;
 
 // We have to dynamically allocate the signal number; see bug 1038900.
 // This function returns the first realtime signal currently set to
 // default handling (i.e., not in use), or 0 if none could be found.
 //
 // WARNING: if this function or anything similar to it (including in
 // external libraries) is used on multiple threads concurrently, there
 // will be a race condition.
@@ -270,23 +275,17 @@ FindFreeSignalNumber()
   return 0;
 }
 
 // Returns true if sandboxing was enabled, or false if sandboxing
 // already was enabled.  Crashes if sandboxing could not be enabled.
 static bool
 SetThreadSandbox()
 {
-  if (prctl(PR_GET_SECCOMP, 0, 0, 0, 0) == 0) {
-    if (!InstallSyscallFilter(gSetSandboxFilter, false)) {
-      MOZ_CRASH("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER)");
-    }
-    return true;
-  }
-  return false;
+  return InstallSyscallFilter(gSetSandboxFilter, false);
 }
 
 static void
 SetThreadSandboxHandler(int signum)
 {
   // The non-zero number sent back to the main thread indicates
   // whether action was taken.
   if (SetThreadSandbox()) {
@@ -446,17 +445,17 @@ static void
 ApplySandboxWithTSync(sock_fprog* aFilter)
 {
   EnterChroot();
   // At this point we're committed to using tsync, because the signal
   // broadcast workaround needs to access procfs.  (Unless chroot
   // isn't used... but this failure shouldn't happen in the first
   // place, so let's not make extra special cases for it.)
   if (!InstallSyscallFilter(aFilter, true)) {
-    MOZ_CRASH("seccomp+tsync failed, but kernel supports tsync");
+    MOZ_CRASH();
   }
 }
 
 // Common code for sandbox startup.
 static void
 SetCurrentProcessSandbox(UniquePtr<sandbox::bpf_dsl::Policy> aPolicy)
 {
   MOZ_ASSERT(gSandboxCrashFunc);
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -3,16 +3,17 @@
 /* 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 "SandboxFilter.h"
 #include "SandboxFilterUtil.h"
 
 #include "SandboxBrokerClient.h"
+#include "SandboxInfo.h"
 #include "SandboxInternal.h"
 #include "SandboxLogging.h"
 
 #include "mozilla/UniquePtr.h"
 
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/ipc.h>
@@ -87,16 +88,26 @@ private:
   // bionic commit 10c8ce59a (in JB and up; API level 16 = Android 4.1).
   static intptr_t TKillCompatTrap(const sandbox::arch_seccomp_data& aArgs,
                                   void *aux)
   {
     return syscall(__NR_tgkill, getpid(), aArgs.args[0], aArgs.args[1]);
   }
 #endif
 
+  static intptr_t SetNoNewPrivsTrap(ArgsRef& aArgs, void* aux) {
+    if (gSetSandboxFilter == nullptr) {
+      // Called after BroadcastSetThreadSandbox finished, therefore
+      // not our doing and not expected.
+      return BlockedSyscallTrap(aArgs, nullptr);
+    }
+    // Signal that the filter is already in place.
+    return -ETXTBSY;
+  }
+
 public:
   virtual ResultExpr InvalidSyscall() const override {
     return Trap(BlockedSyscallTrap, nullptr);
   }
 
   virtual ResultExpr ClonePolicy(ResultExpr failPolicy) const {
     // Allow use for simple thread creation (pthread_create) only.
 
@@ -244,18 +255,26 @@ public:
       return Allow();
 #endif
 #ifdef ANDROID
     case __NR_set_tid_address:
       return Allow();
 #endif
 
       // prctl
-    case __NR_prctl:
-      return PrctlPolicy();
+    case __NR_prctl: {
+      if (SandboxInfo::Get().Test(SandboxInfo::kHasSeccompTSync)) {
+        return PrctlPolicy();
+      }
+
+      Arg<int> option(0);
+      return If(option == PR_SET_NO_NEW_PRIVS,
+                Trap(SetNoNewPrivsTrap, nullptr))
+        .Else(PrctlPolicy());
+    }
 
       // NSPR can call this when creating a thread, but it will accept a
       // polite "no".
     case __NR_getpriority:
       // But if thread creation races with sandbox startup, that call
       // could succeed, and then we get one of these:
     case __NR_setpriority:
       return Error(EACCES);
--- a/security/sandbox/linux/SandboxInternal.h
+++ b/security/sandbox/linux/SandboxInternal.h
@@ -6,20 +6,23 @@
 
 #ifndef mozilla_SandboxInternal_h
 #define mozilla_SandboxInternal_h
 
 #include <signal.h>
 
 #include "mozilla/Types.h"
 
+struct sock_fprog;
+
 namespace mozilla {
 
 // SandboxCrash() has to be in libxul to use internal interfaces, but
 // its caller in libmozsandbox.
 // See also bug 1101170.
 
 typedef void (*SandboxCrashFunc)(int, siginfo_t*, void*);
 extern MOZ_EXPORT SandboxCrashFunc gSandboxCrashFunc;
+extern const sock_fprog* gSetSandboxFilter;
 
 } // namespace mozilla
 
 #endif // mozilla_SandboxInternal_h