summaryrefslogtreecommitdiff
path: root/D146272.diff
diff options
context:
space:
mode:
Diffstat (limited to 'D146272.diff')
-rw-r--r--D146272.diff373
1 files changed, 373 insertions, 0 deletions
diff --git a/D146272.diff b/D146272.diff
new file mode 100644
index 0000000..0a2c749
--- /dev/null
+++ b/D146272.diff
@@ -0,0 +1,373 @@
+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:
+
bgstack15