Bug 1257361 - Simplify detecting threads that already have seccomp-bpf applied. r=tedd r=gcp a=gchang
☠☠ backed out by b54a7014e03d ☠ ☠
authorJed Davis <jld@mozilla.com>
Tue, 06 Dec 2016 12:38:22 -1000
changeset 352895 3a761e5cc19c38782cfc0da0bba68954bd06cd27
parent 352894 9b6dada73f7d8955bc29673e54fc641137a89d3c
child 352896 732bd3ae0bd38156c5286ae56e86ce7c693188ba
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstedd, gcp, gchang
bugs1257361
milestone52.0a2
Bug 1257361 - Simplify detecting threads that already have seccomp-bpf applied. r=tedd r=gcp a=gchang
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