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

--HG--
extra : rebase_source : 4b8459f01e8748fea95cbcb6eeb689f01417ca5b
This commit is contained in:
Jed Davis 2018-01-29 17:36:06 -07:00
Родитель 5fe1b2b553
Коммит 927e70c7a5
3 изменённых файлов: 52 добавлений и 20 удалений

Просмотреть файл

@ -177,7 +177,7 @@ public:
.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:
@ -389,6 +389,26 @@ private:
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
@ -557,6 +577,17 @@ private:
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
@ -601,7 +632,7 @@ public:
~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:
@ -610,8 +641,15 @@ public:
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);
@ -645,7 +683,7 @@ public:
return Some(Allow());
#endif
default:
return SandboxPolicyCommon::EvaluateSocketCall(aCall);
return SandboxPolicyCommon::EvaluateSocketCall(aCall, aHasArgs);
}
}

Просмотреть файл

@ -39,7 +39,7 @@ SandboxPolicyBase::EvaluateSyscall(int aSysno) const {
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)));
@ -65,7 +65,7 @@ SandboxPolicyBase::EvaluateSyscall(int aSysno) const {
#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);

Просмотреть файл

@ -23,7 +23,8 @@ namespace mozilla {
// 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
@ -35,9 +36,13 @@ 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.
@ -45,17 +50,6 @@ public:
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