Blob Blame History Raw
diff --git a/security/sandbox/linux/SandboxFilter.cpp b/security/sandbox/linux/SandboxFilter.cpp
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -128,10 +128,11 @@
   // Subclasses can assign these in their constructors to loosen the
   // default settings.
   SandboxBrokerClient* mBroker = nullptr;
   bool mMayCreateShmem = false;
   bool mAllowUnsafeSocketPair = false;
+  bool mBrokeredConnect = false;  // Can connect() be brokered?
 
   SandboxPolicyCommon() = default;
 
   typedef const sandbox::arch_seccomp_data& ArgsRef;
 
@@ -533,10 +534,124 @@
     MOZ_CRASH("unreachable?");
     return -ENOSYS;
 #endif
   }
 
+  // This just needs to return something to stand in for the
+  // unconnected socket until ConnectTrap, below, and keep track of
+  // the socket type somehow.  Half a socketpair *is* a socket, so it
+  // should result in minimal confusion in the caller.
+  static intptr_t FakeSocketTrapCommon(int domain, int type, int protocol) {
+    int fds[2];
+    // X11 client libs will still try to getaddrinfo() even for a
+    // local connection.  Also, WebRTC still has vestigial network
+    // code trying to do things in the content process.  Politely tell
+    // them no.
+    if (domain != AF_UNIX) {
+      return -EAFNOSUPPORT;
+    }
+    if (socketpair(domain, type, protocol, fds) != 0) {
+      return -errno;
+    }
+    close(fds[1]);
+    return fds[0];
+  }
+
+  static intptr_t FakeSocketTrap(ArgsRef aArgs, void* aux) {
+    return FakeSocketTrapCommon(static_cast<int>(aArgs.args[0]),
+                                static_cast<int>(aArgs.args[1]),
+                                static_cast<int>(aArgs.args[2]));
+  }
+
+  static intptr_t FakeSocketTrapLegacy(ArgsRef aArgs, void* aux) {
+    const auto innerArgs = reinterpret_cast<unsigned long*>(aArgs.args[1]);
+
+    return FakeSocketTrapCommon(static_cast<int>(innerArgs[0]),
+                                static_cast<int>(innerArgs[1]),
+                                static_cast<int>(innerArgs[2]));
+  }
+
+  static Maybe<int> DoGetSockOpt(int fd, int optname) {
+    int optval;
+    socklen_t optlen = sizeof(optval);
+
+    if (getsockopt(fd, SOL_SOCKET, optname, &optval, &optlen) != 0) {
+      return Nothing();
+    }
+    MOZ_RELEASE_ASSERT(static_cast<size_t>(optlen) == sizeof(optval));
+    return Some(optval);
+  }
+
+  // Substitute the newly connected socket from the broker for the
+  // original socket.  This is meant to be used on a fd from
+  // FakeSocketTrap, above, but it should also work to simulate
+  // re-connect()ing a real connected socket.
+  //
+  // Warning: This isn't quite right if the socket is dup()ed, because
+  // other duplicates will still be the original socket, but hopefully
+  // nothing we're dealing with does that.
+  static intptr_t ConnectTrapCommon(SandboxBrokerClient* aBroker, int aFd,
+                                    const struct sockaddr_un* aAddr,
+                                    socklen_t aLen) {
+    if (aFd < 0) {
+      return -EBADF;
+    }
+    const auto maybeDomain = DoGetSockOpt(aFd, SO_DOMAIN);
+    if (!maybeDomain) {
+      return -errno;
+    }
+    if (*maybeDomain != AF_UNIX) {
+      return -EAFNOSUPPORT;
+    }
+    const auto maybeType = DoGetSockOpt(aFd, SO_TYPE);
+    if (!maybeType) {
+      return -errno;
+    }
+    const int oldFlags = fcntl(aFd, F_GETFL);
+    if (oldFlags == -1) {
+      return -errno;
+    }
+    const int newFd = aBroker->Connect(aAddr, aLen, *maybeType);
+    if (newFd < 0) {
+      return newFd;
+    }
+    // Copy over the nonblocking flag.  The connect() won't be
+    // nonblocking in that case, but that shouldn't matter for
+    // AF_UNIX.  The other fcntl-settable flags are either irrelevant
+    // for sockets (e.g., O_APPEND) or would be blocked by this
+    // seccomp-bpf policy, so they're ignored.
+    if (fcntl(newFd, F_SETFL, oldFlags & O_NONBLOCK) != 0) {
+      close(newFd);
+      return -errno;
+    }
+    if (dup2(newFd, aFd) < 0) {
+      close(newFd);
+      return -errno;
+    }
+    close(newFd);
+    return 0;
+  }
+
+  static intptr_t ConnectTrap(ArgsRef aArgs, void* aux) {
+    typedef const struct sockaddr_un* AddrPtr;
+
+    return ConnectTrapCommon(static_cast<SandboxBrokerClient*>(aux),
+                             static_cast<int>(aArgs.args[0]),
+                             reinterpret_cast<AddrPtr>(aArgs.args[1]),
+                             static_cast<socklen_t>(aArgs.args[2]));
+  }
+
+  static intptr_t ConnectTrapLegacy(ArgsRef aArgs, void* aux) {
+    const auto innerArgs = reinterpret_cast<unsigned long*>(aArgs.args[1]);
+    typedef const struct sockaddr_un* AddrPtr;
+
+    return ConnectTrapCommon(static_cast<SandboxBrokerClient*>(aux),
+                             static_cast<int>(innerArgs[0]),
+                             reinterpret_cast<AddrPtr>(innerArgs[1]),
+                             static_cast<socklen_t>(innerArgs[2]));
+  }
+
  public:
   ResultExpr InvalidSyscall() const override {
     return Trap(BlockedSyscallTrap, nullptr);
   }
 
@@ -630,15 +745,37 @@
           return Some(Allow());
         }
         Arg<int> level(1), optname(2);
         // SO_SNDBUF is used by IPC to avoid constructing
         // unnecessarily large gather arrays for `sendmsg`.
-        return Some(
-            If(AllOf(level == SOL_SOCKET, optname == SO_SNDBUF), Allow())
-                .Else(InvalidSyscall()));
+        //
+        // SO_DOMAIN and SO_TYPE are needed for connect() brokering,
+        // but they're harmless even when it's not enabled.
+        return Some(If(AllOf(level == SOL_SOCKET,
+                             AnyOf(optname == SO_SNDBUF, optname == SO_DOMAIN,
+                                   optname == SO_TYPE)),
+                       Allow())
+                        .Else(InvalidSyscall()));
       }
 
+        // These two cases are for connect() brokering, if enabled.
+      case SYS_SOCKET:
+        if (mBrokeredConnect) {
+          const auto trapFn = aHasArgs ? FakeSocketTrap : FakeSocketTrapLegacy;
+          MOZ_ASSERT(mBroker);
+          return Some(Trap(trapFn, mBroker));
+        }
+        return Nothing();
+
+      case SYS_CONNECT:
+        if (mBrokeredConnect) {
+          const auto trapFn = aHasArgs ? ConnectTrap : ConnectTrapLegacy;
+          MOZ_ASSERT(mBroker);
+          return Some(Trap(trapFn, mBroker));
+        }
+        return Nothing();
+
       default:
         return Nothing();
     }
   }
 
@@ -1006,10 +1143,16 @@
         return If(AnyOf(request == TCGETS, request == TIOCGWINSZ),
                   Error(ENOTTY))
             .Else(SandboxPolicyBase::EvaluateSyscall(sysno));
       }
 
+      CASES_FOR_dup2:  // See ConnectTrapCommon
+        if (mBrokeredConnect) {
+          return Allow();
+        }
+        return SandboxPolicyBase::EvaluateSyscall(sysno);
+
 #ifdef MOZ_ASAN
         // ASAN's error reporter wants to know if stderr is a tty.
       case __NR_ioctl: {
         Arg<int> fd(0);
         return If(fd == STDERR_FILENO, Error(ENOTTY)).Else(InvalidSyscall());
@@ -1093,133 +1236,20 @@
 
     close(fd);
     return rv;
   }
 
-  // This just needs to return something to stand in for the
-  // unconnected socket until ConnectTrap, below, and keep track of
-  // the socket type somehow.  Half a socketpair *is* a socket, so it
-  // should result in minimal confusion in the caller.
-  static intptr_t FakeSocketTrapCommon(int domain, int type, int protocol) {
-    int fds[2];
-    // X11 client libs will still try to getaddrinfo() even for a
-    // local connection.  Also, WebRTC still has vestigial network
-    // code trying to do things in the content process.  Politely tell
-    // them no.
-    if (domain != AF_UNIX) {
-      return -EAFNOSUPPORT;
-    }
-    if (socketpair(domain, type, protocol, fds) != 0) {
-      return -errno;
-    }
-    close(fds[1]);
-    return fds[0];
-  }
-
-  static intptr_t FakeSocketTrap(ArgsRef aArgs, void* aux) {
-    return FakeSocketTrapCommon(static_cast<int>(aArgs.args[0]),
-                                static_cast<int>(aArgs.args[1]),
-                                static_cast<int>(aArgs.args[2]));
-  }
-
-  static intptr_t FakeSocketTrapLegacy(ArgsRef aArgs, void* aux) {
-    const auto innerArgs = reinterpret_cast<unsigned long*>(aArgs.args[1]);
-
-    return FakeSocketTrapCommon(static_cast<int>(innerArgs[0]),
-                                static_cast<int>(innerArgs[1]),
-                                static_cast<int>(innerArgs[2]));
-  }
-
-  static Maybe<int> DoGetSockOpt(int fd, int optname) {
-    int optval;
-    socklen_t optlen = sizeof(optval);
-
-    if (getsockopt(fd, SOL_SOCKET, optname, &optval, &optlen) != 0) {
-      return Nothing();
-    }
-    MOZ_RELEASE_ASSERT(static_cast<size_t>(optlen) == sizeof(optval));
-    return Some(optval);
-  }
-
-  // Substitute the newly connected socket from the broker for the
-  // original socket.  This is meant to be used on a fd from
-  // FakeSocketTrap, above, but it should also work to simulate
-  // re-connect()ing a real connected socket.
-  //
-  // Warning: This isn't quite right if the socket is dup()ed, because
-  // other duplicates will still be the original socket, but hopefully
-  // nothing we're dealing with does that.
-  static intptr_t ConnectTrapCommon(SandboxBrokerClient* aBroker, int aFd,
-                                    const struct sockaddr_un* aAddr,
-                                    socklen_t aLen) {
-    if (aFd < 0) {
-      return -EBADF;
-    }
-    const auto maybeDomain = DoGetSockOpt(aFd, SO_DOMAIN);
-    if (!maybeDomain) {
-      return -errno;
-    }
-    if (*maybeDomain != AF_UNIX) {
-      return -EAFNOSUPPORT;
-    }
-    const auto maybeType = DoGetSockOpt(aFd, SO_TYPE);
-    if (!maybeType) {
-      return -errno;
-    }
-    const int oldFlags = fcntl(aFd, F_GETFL);
-    if (oldFlags == -1) {
-      return -errno;
-    }
-    const int newFd = aBroker->Connect(aAddr, aLen, *maybeType);
-    if (newFd < 0) {
-      return newFd;
-    }
-    // Copy over the nonblocking flag.  The connect() won't be
-    // nonblocking in that case, but that shouldn't matter for
-    // AF_UNIX.  The other fcntl-settable flags are either irrelevant
-    // for sockets (e.g., O_APPEND) or would be blocked by this
-    // seccomp-bpf policy, so they're ignored.
-    if (fcntl(newFd, F_SETFL, oldFlags & O_NONBLOCK) != 0) {
-      close(newFd);
-      return -errno;
-    }
-    if (dup2(newFd, aFd) < 0) {
-      close(newFd);
-      return -errno;
-    }
-    close(newFd);
-    return 0;
-  }
-
-  static intptr_t ConnectTrap(ArgsRef aArgs, void* aux) {
-    typedef const struct sockaddr_un* AddrPtr;
-
-    return ConnectTrapCommon(static_cast<SandboxBrokerClient*>(aux),
-                             static_cast<int>(aArgs.args[0]),
-                             reinterpret_cast<AddrPtr>(aArgs.args[1]),
-                             static_cast<socklen_t>(aArgs.args[2]));
-  }
-
-  static intptr_t ConnectTrapLegacy(ArgsRef aArgs, void* aux) {
-    const auto innerArgs = reinterpret_cast<unsigned long*>(aArgs.args[1]);
-    typedef const struct sockaddr_un* AddrPtr;
-
-    return ConnectTrapCommon(static_cast<SandboxBrokerClient*>(aux),
-                             static_cast<int>(innerArgs[0]),
-                             reinterpret_cast<AddrPtr>(innerArgs[1]),
-                             static_cast<socklen_t>(innerArgs[2]));
-  }
-
  public:
   ContentSandboxPolicy(SandboxBrokerClient* aBroker,
                        ContentProcessSandboxParams&& aParams)
       : mParams(std::move(aParams)),
         mAllowSysV(PR_GetEnv("MOZ_SANDBOX_ALLOW_SYSV") != nullptr),
         mUsingRenderDoc(PR_GetEnv("RENDERDOC_CAPTUREOPTS") != nullptr) {
     mBroker = aBroker;
     mMayCreateShmem = true;
     mAllowUnsafeSocketPair = true;
+    mBrokeredConnect = true;
   }
 
   ~ContentSandboxPolicy() override = default;
 
   Maybe<ResultExpr> EvaluateSocketCall(int aCall,
@@ -1232,18 +1262,16 @@
 
 #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)));
-      }
-      case SYS_CONNECT: {
-        const auto trapFn = aHasArgs ? ConnectTrap : ConnectTrapLegacy;
-        return Some(AllowBelowLevel(4, Trap(trapFn, mBroker)));
-      }
+      case SYS_SOCKET:
+      case SYS_CONNECT:
+        if (BelowLevel(4)) {
+          return Some(Allow());
+        }
+        return SandboxPolicyCommon::EvaluateSocketCall(aCall, aHasArgs);
       case SYS_RECV:
       case SYS_SEND:
       case SYS_GETSOCKOPT:
       case SYS_SETSOCKOPT:
       case SYS_GETSOCKNAME:
@@ -1458,13 +1486,10 @@
 
       case __NR_getrusage:
       case __NR_times:
         return Allow();
 
-      CASES_FOR_dup2:  // See ConnectTrapCommon
-        return Allow();
-
       case __NR_fsync:
       case __NR_msync:
         return Allow();
 
       case __NR_getpriority: