Bug 1425274 - Filter socketpair() in content sandbox on 32-bit x86 with new-enough kernels. r?gcp draft
authorJed Davis <jld@mozilla.com>
Mon, 29 Jan 2018 17:36:06 -0700
changeset 748917 d40f66be8fdee2944aa539a1332e492649f65658
parent 748916 9f17734fe3719eee3f5e6a2572a9aa77d624dd62
push id97274
push userbmo:jld@mozilla.com
push dateTue, 30 Jan 2018 17:43:04 +0000
reviewersgcp
bugs1425274, 1273852
milestone60.0a1
Bug 1425274 - Filter socketpair() in content sandbox on 32-bit x86 with new-enough kernels. r?gcp This replaces the globals for whether socket calls (and ipc(2) calls, but we never used that) have real arguments with a parameter, which in hindsight should have been done in bug 1273852, which is when we started handling both socketcall(2) and separate socket calls in the same policy. This allows handling the two cases differently. MozReview-Commit-ID: 1pfckmCpJlW
security/sandbox/linux/SandboxFilter.cpp
security/sandbox/linux/SandboxFilterUtil.cpp
security/sandbox/linux/SandboxFilterUtil.h
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -172,17 +172,17 @@ public:
       .CASES((PR_GET_SECCOMP, // BroadcastSetThreadSandbox, etc.
               PR_SET_NAME,    // Thread creation
               PR_SET_DUMPABLE, // Crash reporting
               PR_SET_PTRACER), // Debug-mode crash handling
              Allow())
       .Default(InvalidSyscall());
   }
 
-  Maybe<ResultExpr> EvaluateSocketCall(int aCall) const override {
+  Maybe<ResultExpr> EvaluateSocketCall(int aCall, bool aHasArgs) const override {
     switch (aCall) {
     case SYS_RECVMSG:
     case SYS_SENDMSG:
       return Some(Allow());
     default:
       return Nothing();
     }
   }
@@ -384,16 +384,36 @@ private:
   }
   ResultExpr AllowBelowLevel(int aLevel, ResultExpr aOrElse) const {
     return BelowLevel(aLevel) ? Allow() : Move(aOrElse);
   }
   ResultExpr AllowBelowLevel(int aLevel) const {
     return AllowBelowLevel(aLevel, InvalidSyscall());
   }
 
+  // Returns true if the running kernel supports separate syscalls for
+  // socket operations, or false if it supports only socketcall(2).
+  static bool
+  HasSeparateSocketCalls() {
+#ifdef __NR_socket
+    // If there's no socketcall, then obviously there are separate syscalls.
+#ifdef __NR_socketcall
+    int fd = syscall(__NR_socket, AF_LOCAL, SOCK_STREAM, 0);
+    if (fd < 0) {
+      MOZ_DIAGNOSTIC_ASSERT(errno == ENOSYS);
+      return false;
+    }
+    close(fd);
+#endif // __NR_socketcall
+    return true;
+#else // ifndef __NR_socket
+    return false;
+#endif // __NR_socket
+  }
+
   // Trap handlers for filesystem brokering.
   // (The amount of code duplication here could be improved....)
 #ifdef __NR_open
   static intptr_t OpenTrap(ArgsRef aArgs, void* aux) {
     auto broker = static_cast<SandboxBrokerClient*>(aux);
     auto path = reinterpret_cast<const char*>(aArgs.args[0]);
     auto flags = static_cast<int>(aArgs.args[1]);
     return broker->Open(path, flags);
@@ -552,16 +572,27 @@ private:
 
   static intptr_t SocketpairDatagramTrap(ArgsRef aArgs, void* aux) {
     auto fds = reinterpret_cast<int*>(aArgs.args[3]);
     // Return sequential packet sockets instead of the expected
     // datagram sockets; see bug 1355274 for details.
     return ConvertError(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
   }
 
+  static intptr_t SocketpairUnpackTrap(ArgsRef aArgs, void* aux) {
+#ifdef __NR_socketpair
+    auto argsPtr = reinterpret_cast<unsigned long*>(aArgs.args[1]);
+    return DoSyscall(__NR_socketpair, argsPtr[0], argsPtr[1], argsPtr[2],
+                     argsPtr[3]);
+#else
+    MOZ_CRASH("unreachable?");
+    return -ENOSYS;
+#endif
+  }
+
   static intptr_t StatFsTrap(ArgsRef aArgs, void* aux) {
     // Warning: the kernel interface is not the C interface.  The
     // structs are different (<asm/statfs.h> vs. <sys/statfs.h>), and
     // the statfs64 version takes an additional size parameter.
     auto path = reinterpret_cast<const char*>(aArgs.args[0]);
     int fd = open(path, O_RDONLY | O_LARGEFILE);
     if (fd < 0) {
       return -errno;
@@ -596,27 +627,34 @@ public:
                        ContentProcessSandboxParams&& aParams)
     : mBroker(aBroker)
     , mParams(Move(aParams))
     , mAllowSysV(PR_GetEnv("MOZ_SANDBOX_ALLOW_SYSV") != nullptr)
     { }
 
   ~ContentSandboxPolicy() override = default;
 
-  Maybe<ResultExpr> EvaluateSocketCall(int aCall) const override {
+  Maybe<ResultExpr> EvaluateSocketCall(int aCall, bool aHasArgs) const override {
     switch(aCall) {
     case SYS_RECVFROM:
     case SYS_SENDTO:
     case SYS_SENDMMSG: // libresolv via libasyncns; see bug 1355274
       return Some(Allow());
 
     case SYS_SOCKETPAIR: {
       // See bug 1066750.
-      if (!kSocketCallHasArgs) {
-        // We can't filter the args if the platform passes them by pointer.
+      if (!aHasArgs) {
+        // If this is a socketcall(2) platform, but the kernel also
+        // supports separate syscalls (>= 4.2.0), we can unpack the
+        // arguments and filter them.
+        if (HasSeparateSocketCalls()) {
+          return Some(Trap(SocketpairUnpackTrap, nullptr));
+        }
+        // Otherwise, we can't filter the args if the platform passes
+        // them by pointer.
         return Some(Allow());
       }
       Arg<int> domain(0), type(1);
       return Some(If(domain == AF_UNIX,
                      Switch(type & ~SOCK_CLOEXEC)
                      .Case(SOCK_STREAM, Allow())
                      .Case(SOCK_SEQPACKET, Allow())
                      .Case(SOCK_DGRAM, Trap(SocketpairDatagramTrap, nullptr))
@@ -640,17 +678,17 @@ public:
     case SYS_GETSOCKOPT:
     case SYS_SETSOCKOPT:
     case SYS_GETSOCKNAME:
     case SYS_GETPEERNAME:
     case SYS_SHUTDOWN:
       return Some(Allow());
 #endif
     default:
-      return SandboxPolicyCommon::EvaluateSocketCall(aCall);
+      return SandboxPolicyCommon::EvaluateSocketCall(aCall, aHasArgs);
     }
   }
 
 #ifdef DESKTOP
   Maybe<ResultExpr> EvaluateIpcCall(int aCall) const override {
     switch(aCall) {
       // These are a problem: SysV shared memory follows the Unix
       // "same uid policy" and can't be restricted/brokered like file
--- a/security/sandbox/linux/SandboxFilterUtil.cpp
+++ b/security/sandbox/linux/SandboxFilterUtil.cpp
@@ -34,17 +34,17 @@ namespace mozilla {
 sandbox::bpf_dsl::ResultExpr
 SandboxPolicyBase::EvaluateSyscall(int aSysno) const {
   switch (aSysno) {
 #ifdef __NR_socketcall
     case __NR_socketcall: {
       Arg<int> call(0);
       UniquePtr<Caser<int>> acc(new Caser<int>(Switch(call)));
       for (int i = SYS_SOCKET; i <= SYS_SENDMMSG; ++i) {
-        auto thisCase = EvaluateSocketCall(i);
+        auto thisCase = EvaluateSocketCall(i, false);
         // Optimize out cases that are equal to the default.
         if (thisCase) {
           acc.reset(new Caser<int>(acc->Case(i, *thisCase)));
         }
       }
       return acc->Default(InvalidSyscall());
     }
 #ifndef ANDROID
@@ -60,17 +60,17 @@ SandboxPolicyBase::EvaluateSyscall(int a
         }
       }
       return acc->Default(InvalidSyscall());
     }
 #endif // ANDROID
 #endif // __NR_socketcall
 #define DISPATCH_SOCKETCALL(sysnum, socketnum)                       \
     case sysnum:                                                     \
-      return EvaluateSocketCall(socketnum).valueOr(InvalidSyscall())
+      return EvaluateSocketCall(socketnum, true).valueOr(InvalidSyscall())
 #ifdef __NR_socket
       DISPATCH_SOCKETCALL(__NR_socket,      SYS_SOCKET);
       DISPATCH_SOCKETCALL(__NR_bind,        SYS_BIND);
       DISPATCH_SOCKETCALL(__NR_connect,     SYS_CONNECT);
       DISPATCH_SOCKETCALL(__NR_listen,      SYS_LISTEN);
 #ifdef __NR_accept
       DISPATCH_SOCKETCALL(__NR_accept,      SYS_ACCEPT);
 #endif
--- a/security/sandbox/linux/SandboxFilterUtil.h
+++ b/security/sandbox/linux/SandboxFilterUtil.h
@@ -18,49 +18,43 @@
 
 namespace mozilla {
 
 // This class handles syscalls for BSD socket and SysV IPC operations.
 // On 32-bit x86 they're multiplexed via socketcall(2) and ipc(2),
 // respectively; on most other architectures they're individual system
 // calls. It translates the syscalls into socketcall/ipc selector
 // values, because those are defined (even if not used) for all
-// architectures.
+// architectures.  (As of kernel 4.2.0, x86 also has regular system
+// calls, but userland will typically still use socketcall.)
 //
 // This EvaluateSyscall() routine always returns InvalidSyscall() for
 // everything else.  It's assumed that subclasses will be implementing
 // a whitelist policy, so they can handle what they're whitelisting
 // and then defer to this class in the default case.
 class SandboxPolicyBase : public sandbox::bpf_dsl::Policy
 {
 public:
   using ResultExpr = sandbox::bpf_dsl::ResultExpr;
 
   virtual ResultExpr EvaluateSyscall(int aSysno) const override;
-  virtual Maybe<ResultExpr> EvaluateSocketCall(int aCall) const {
+
+  // aHasArgs is true if this is a normal syscall, where the arguments
+  // can be inspected by seccomp-bpf, rather than a case of socketcall().
+  virtual Maybe<ResultExpr> EvaluateSocketCall(int aCall, bool aHasArgs) const {
     return Nothing();
   }
+
 #ifndef ANDROID
   // Android doesn't use SysV IPC (and doesn't define the selector
   // constants in its headers), so this isn't implemented there.
   virtual Maybe<ResultExpr> EvaluateIpcCall(int aCall) const {
     return Nothing();
   }
 #endif
-
-#ifdef __NR_socketcall
-  // socketcall(2) takes the actual call's arguments via a pointer, so
-  // seccomp-bpf can't inspect them; ipc(2) takes them at different indices.
-  static const bool kSocketCallHasArgs = false;
-  static const bool kIpcCallNormalArgs = false;
-#else
-  // Otherwise, the bpf_dsl Arg<> class can be used normally.
-  static const bool kSocketCallHasArgs = true;
-  static const bool kIpcCallNormalArgs = true;
-#endif
 };
 
 } // namespace mozilla
 
 // "Machine independent" pseudo-syscall numbers, to deal with arch
 // dependencies.  (Most 32-bit archs started with 32-bit off_t; older
 // archs started with 16-bit uid_t/gid_t; 32-bit registers can't hold
 // a 64-bit offset for mmap; and so on.)