diff options
Diffstat (limited to 'D146272.diff')
-rw-r--r-- | D146272.diff | 373 |
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: + |