Bug 1511560 - Move the socketpair handling into SandboxPolicyCommon. r=gcp
authorJed Davis <jld@mozilla.com>
Sat, 23 Feb 2019 00:44:10 +0000
changeset 519402 db2dee78ddb0dd23e29948258abd6c7404555b59
parent 519401 bab79f85596242146787d6d2a5ad56596cc1343e
child 519403 94cb1fe9db5eb0f0aa0634541afb08af17cf5c05
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgcp
bugs1511560
milestone67.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 1511560 - Move the socketpair handling into SandboxPolicyCommon. r=gcp The sandbox broker uses socketpair to construct the per-request channels over which responses are sent; thus, if and only if the policy will be using brokering, it will allow socketpair as safely as possible (i.e., denying datagram sockets if possible). Depends on D14522 Differential Revision: https://phabricator.services.mozilla.com/D14523
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
@@ -287,16 +287,34 @@ class SandboxPolicyCommon : public Sandb
     if (fd != AT_FDCWD && path[0] != '/') {
       SANDBOX_LOG_ERROR("unsupported fd-relative readlinkat(%d, %s, %p, %u)",
                         fd, path, buf, size);
       return BlockedSyscallTrap(aArgs, nullptr);
     }
     return broker->Readlink(path, buf, size);
   }
 
+  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
+  }
+
  public:
   ResultExpr InvalidSyscall() const override {
     return Trap(BlockedSyscallTrap, nullptr);
   }
 
   virtual ResultExpr ClonePolicy(ResultExpr failPolicy) const {
     // Allow use for simple thread creation (pthread_create) only.
 
@@ -336,16 +354,49 @@ class SandboxPolicyCommon : public Sandb
   }
 
   Maybe<ResultExpr> EvaluateSocketCall(int aCall,
                                        bool aHasArgs) const override {
     switch (aCall) {
       case SYS_RECVMSG:
       case SYS_SENDMSG:
         return Some(Allow());
+
+      case SYS_SOCKETPAIR: {
+        // Allow "safe" (always connected) socketpairs when using the
+        // file broker.
+        if (mBroker == nullptr) {
+          return Nothing();
+        }
+        // See bug 1066750.
+        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 | SOCK_NONBLOCK))
+                   .Case(SOCK_STREAM, Allow())
+                   .Case(SOCK_SEQPACKET, Allow())
+                   // This is used only by content (and only for
+                   // direct PulseAudio, which is deprecated) but it
+                   // doesn't increase attack surface:
+                   .Case(SOCK_DGRAM, Trap(SocketpairDatagramTrap, nullptr))
+                   .Default(InvalidSyscall()))
+                .Else(InvalidSyscall()));
+      }
+
       default:
         return Nothing();
     }
   }
 
   ResultExpr EvaluateSyscall(int sysno) const override {
     // If a file broker client was provided, route syscalls to it;
     // otherwise, fall through to the main policy, which will deny
@@ -596,60 +647,23 @@ class ContentSandboxPolicy : public Sand
   bool BelowLevel(int aLevel) const { return mParams.mLevel < aLevel; }
   ResultExpr AllowBelowLevel(int aLevel, ResultExpr aOrElse) const {
     return BelowLevel(aLevel) ? Allow() : std::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
-  }
-
   static intptr_t GetPPidTrap(ArgsRef aArgs, void* aux) {
     // In a pid namespace, getppid() will return 0. We will return 0 instead
     // of the real parent pid to see what breaks when we introduce the
     // pid namespace (Bug 1151624).
     return 0;
   }
 
-  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;
@@ -806,40 +820,16 @@ class ContentSandboxPolicy : public Sand
   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 (!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 | SOCK_NONBLOCK))
-                   .Case(SOCK_STREAM, Allow())
-                   .Case(SOCK_SEQPACKET, Allow())
-                   .Case(SOCK_DGRAM, Trap(SocketpairDatagramTrap, nullptr))
-                   .Default(InvalidSyscall()))
-                .Else(InvalidSyscall()));
-      }
-
 #  ifdef ANDROID
       case SYS_SOCKET:
         return Some(Error(EACCES));
 #  else  // #ifdef DESKTOP
       case SYS_SOCKET: {
         const auto trapFn = aHasArgs ? FakeSocketTrap : FakeSocketTrapLegacy;
         return Some(AllowBelowLevel(4, Trap(trapFn, nullptr)));
       }
--- a/security/sandbox/linux/SandboxFilterUtil.cpp
+++ b/security/sandbox/linux/SandboxFilterUtil.cpp
@@ -5,16 +5,19 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SandboxFilterUtil.h"
 
 #ifndef ANDROID
 #  include <linux/ipc.h>
 #endif
 #include <linux/net.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <unistd.h>
 
 #include "mozilla/UniquePtr.h"
 #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
 
 // Older kernel headers (mostly Android, but also some older desktop
 // distributions) are missing some or all of these:
 #ifndef SYS_ACCEPT4
 #  define SYS_ACCEPT4 18
@@ -115,9 +118,28 @@ sandbox::bpf_dsl::ResultExpr SandboxPoli
 #endif  // ANDROID
 #endif  // __NR_socketcall
           // clang-format on
     default:
       return InvalidSyscall();
   }
 }
 
+/* static */ bool SandboxPolicyBase::HasSeparateSocketCalls() {
+#ifdef __NR_socket
+  // If there's no socketcall, then obviously there are separate syscalls.
+#  ifdef __NR_socketcall
+  // This could be memoized, but currently it's called at most once
+  // per process.
+  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
+}
+
 }  // namespace mozilla
--- a/security/sandbox/linux/SandboxFilterUtil.h
+++ b/security/sandbox/linux/SandboxFilterUtil.h
@@ -44,16 +44,20 @@ class SandboxPolicyBase : public sandbox
 
 #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
+
+  // Returns true if the running kernel supports separate syscalls for
+  // socket operations, or false if it supports only socketcall(2).
+  static bool HasSeparateSocketCalls();
 };
 
 }  // 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.)