summaryrefslogtreecommitdiffstats
path: root/sandbox
diff options
context:
space:
mode:
authormarkus@chromium.org <markus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-20 11:50:50 +0000
committermarkus@chromium.org <markus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-20 11:50:50 +0000
commita301233667339ec1a231c5cf60fb58c2e80adf90 (patch)
treeaf2dc00574e90a72cadbe71b6fd91a4e73290a3b /sandbox
parent91c92c8a01c1f376f0ce8881fd1d800f2aa1ca1e (diff)
downloadchromium_src-a301233667339ec1a231c5cf60fb58c2e80adf90.zip
chromium_src-a301233667339ec1a231c5cf60fb58c2e80adf90.tar.gz
chromium_src-a301233667339ec1a231c5cf60fb58c2e80adf90.tar.bz2
SECCOMP-BPF: Added support for checking system call arguments against bit masks.
BUG=130662 TEST=sandbox_linux_unittests Review URL: https://chromiumcodereview.appspot.com/11613016 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@174135 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r--sandbox/linux/seccomp-bpf/codegen.cc15
-rw-r--r--sandbox/linux/seccomp-bpf/errorcode.h47
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf.cc118
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf.h7
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc478
-rw-r--r--sandbox/linux/seccomp-bpf/verifier.cc104
-rw-r--r--sandbox/linux/seccomp-bpf/verifier.h2
7 files changed, 752 insertions, 19 deletions
diff --git a/sandbox/linux/seccomp-bpf/codegen.cc b/sandbox/linux/seccomp-bpf/codegen.cc
index 649793c..4fab5c8 100644
--- a/sandbox/linux/seccomp-bpf/codegen.cc
+++ b/sandbox/linux/seccomp-bpf/codegen.cc
@@ -79,6 +79,21 @@ void CodeGen::PrintProgram(const Sandbox::Program& program) {
case BPF_RET:
fprintf(stderr, "RET 0x%x\n", iter->k);
break;
+ case BPF_ALU:
+ fprintf(stderr, BPF_OP(iter->code) == BPF_NEG
+ ? "A := -A\n" : "A := A %s 0x%x\n",
+ BPF_OP(iter->code) == BPF_ADD ? "+" :
+ BPF_OP(iter->code) == BPF_SUB ? "-" :
+ BPF_OP(iter->code) == BPF_MUL ? "*" :
+ BPF_OP(iter->code) == BPF_DIV ? "/" :
+ BPF_OP(iter->code) == BPF_MOD ? "%" :
+ BPF_OP(iter->code) == BPF_OR ? "|" :
+ BPF_OP(iter->code) == BPF_XOR ? "^" :
+ BPF_OP(iter->code) == BPF_AND ? "&" :
+ BPF_OP(iter->code) == BPF_LSH ? "<<" :
+ BPF_OP(iter->code) == BPF_RSH ? ">>" : "???",
+ (int)iter->k);
+ break;
default:
fprintf(stderr, "???\n");
break;
diff --git a/sandbox/linux/seccomp-bpf/errorcode.h b/sandbox/linux/seccomp-bpf/errorcode.h
index d2661db..67890c6 100644
--- a/sandbox/linux/seccomp-bpf/errorcode.h
+++ b/sandbox/linux/seccomp-bpf/errorcode.h
@@ -20,11 +20,17 @@ struct arch_seccomp_data;
class ErrorCode {
public:
enum {
- // Allow this system call.
- ERR_ALLOWED = 0x0000,
+ // Allow this system call. The value of ERR_ALLOWED is pretty much
+ // completely arbitrary. But we want to pick it so that is is unlikely
+ // to be passed in accidentally, when the user intended to return an
+ // "errno" (see below) value instead.
+ ERR_ALLOWED = 0x04000000,
// Deny the system call with a particular "errno" value.
- ERR_MIN_ERRNO = 1,
+ // N.B.: It is also possible to return "0" here. That would normally
+ // indicate success, but it won't actually run the system call.
+ // This is very different from return ERR_ALLOWED.
+ ERR_MIN_ERRNO = 0,
ERR_MAX_ERRNO = 4095,
// This code should never be used directly, it is used internally only.
@@ -42,11 +48,42 @@ class ErrorCode {
typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void *aux);
enum ArgType {
- TP_32BIT, TP_64BIT,
+ // A conditional test should operate on the 32bit part of the system call
+ // argument.
+ // On 64bit architectures, this verifies that the caller did not actually
+ // pass in a 64bit value. If they did, that will be interpreted as an
+ // attempt at breaking the sandbox and results in the program getting
+ // terminated.
+ // In other words, only perform a 32bit test, if you are sure this
+ // particular system call would never legitimately take a 64bit argument.
+ TP_32BIT,
+
+ // A conditional test should operate on 64bit arguments. It is harmless
+ // to perform a 64bit test on a 32bit system, as the top 32 bits of all
+ // arguments will always be zero.
+ TP_64BIT,
};
enum Operation {
- OP_EQUAL, OP_GREATER, OP_GREATER_EQUAL, OP_HAS_BITS,
+ // Test whether the system call argument is equal to the operand.
+ OP_EQUAL,
+
+ // Test whether the system call argument is greater (or equal) to the
+ // operand. Please note that all tests always operate on unsigned
+ // values. You can generally emulate signed tests, if that's what you
+ // need.
+ // TODO(markus): Check whether we should automatically emulate signed
+ // operations.
+ OP_GREATER_UNSIGNED, OP_GREATER_EQUAL_UNSIGNED,
+
+ // Tests a system call argument against a bit mask.
+ // The "ALL_BITS" variant performs this test: "arg & mask == mask"
+ // This implies that a mask of zero always results in a passing test.
+ // The "ANY_BITS" variant performs this test: "arg & mask != 0"
+ // This implies that a mask of zero always results in a failing test.
+ OP_HAS_ALL_BITS, OP_HAS_ANY_BITS,
+
+ // Total number of operations.
OP_NUM_OPS,
};
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
index 366706f..130845f 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -60,6 +60,17 @@ void SetIsInSigHandler() {
sigprocmask(SIG_BLOCK, &mask, NULL);
}
+template<class T> int popcount(T x);
+template<> int popcount<unsigned int>(unsigned int x) {
+ return __builtin_popcount(x);
+}
+template<> int popcount<unsigned long>(unsigned long x) {
+ return __builtin_popcountl(x);
+}
+template<> int popcount<unsigned long long>(unsigned long long x) {
+ return __builtin_popcountll(x);
+}
+
} // namespace
// The kernel gives us a sandbox, we turn it into a playground :-)
@@ -748,13 +759,114 @@ Instruction *Sandbox::CondExpression(CodeGen *gen, const ErrorCode& cond) {
if (cond.width_ == ErrorCode::TP_64BIT) {
msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
static_cast<uint32_t>(cond.value_ >> 32),
- NULL,
+ lsb_head,
RetExpression(gen, *cond.failed_));
gen->JoinInstructions(msb_head, msb_tail);
}
break;
+ case ErrorCode::OP_HAS_ALL_BITS:
+ // Check the bits in the LSB half of the system call argument. Our
+ // OP_HAS_ALL_BITS operator passed, iff all of the bits are set. This is
+ // different from the kernel's BPF_JSET operation which passes, if any of
+ // the bits are set.
+ // Of course, if there is only a single set bit (or none at all), then
+ // things get easier.
+ {
+ uint32_t lsb_bits = static_cast<uint32_t>(cond.value_);
+ int lsb_bit_count = popcount(lsb_bits);
+ if (lsb_bit_count == 0) {
+ // No bits are set in the LSB half. The test will always pass.
+ lsb_head = RetExpression(gen, *cond.passed_);
+ lsb_tail = NULL;
+ } else if (lsb_bit_count == 1) {
+ // Exactly one bit is set in the LSB half. We can use the BPF_JSET
+ // operator.
+ lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
+ lsb_bits,
+ RetExpression(gen, *cond.passed_),
+ RetExpression(gen, *cond.failed_));
+ gen->JoinInstructions(lsb_head, lsb_tail);
+ } else {
+ // More than one bit is set in the LSB half. We need to combine
+ // BPF_AND and BPF_JEQ to test whether all of these bits are in fact
+ // set in the system call argument.
+ gen->JoinInstructions(lsb_head,
+ gen->MakeInstruction(BPF_ALU+BPF_AND+BPF_K,
+ lsb_bits,
+ lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
+ lsb_bits,
+ RetExpression(gen, *cond.passed_),
+ RetExpression(gen, *cond.failed_))));
+ }
+ }
+
+ // If we are looking at a 64bit argument, we need to also check the bits
+ // in the MSB half of the system call argument.
+ if (cond.width_ == ErrorCode::TP_64BIT) {
+ uint32_t msb_bits = static_cast<uint32_t>(cond.value_ >> 32);
+ int msb_bit_count = popcount(msb_bits);
+ if (msb_bit_count == 0) {
+ // No bits are set in the MSB half. The test will always pass.
+ msb_head = lsb_head;
+ } else if (msb_bit_count == 1) {
+ // Exactly one bit is set in the MSB half. We can use the BPF_JSET
+ // operator.
+ msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
+ msb_bits,
+ lsb_head,
+ RetExpression(gen, *cond.failed_));
+ gen->JoinInstructions(msb_head, msb_tail);
+ } else {
+ // More than one bit is set in the MSB half. We need to combine
+ // BPF_AND and BPF_JEQ to test whether all of these bits are in fact
+ // set in the system call argument.
+ gen->JoinInstructions(msb_head,
+ gen->MakeInstruction(BPF_ALU+BPF_AND+BPF_K,
+ msb_bits,
+ gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
+ msb_bits,
+ lsb_head,
+ RetExpression(gen, *cond.failed_))));
+ }
+ }
+ break;
+ case ErrorCode::OP_HAS_ANY_BITS:
+ // Check the bits in the LSB half of the system call argument. Our
+ // OP_HAS_ANY_BITS operator passed, iff any of the bits are set. This maps
+ // nicely to the kernel's BPF_JSET operation.
+ {
+ uint32_t lsb_bits = static_cast<uint32_t>(cond.value_);
+ if (!lsb_bits) {
+ // No bits are set in the LSB half. The test will always fail.
+ lsb_head = RetExpression(gen, *cond.failed_);
+ lsb_tail = NULL;
+ } else {
+ lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
+ lsb_bits,
+ RetExpression(gen, *cond.passed_),
+ RetExpression(gen, *cond.failed_));
+ gen->JoinInstructions(lsb_head, lsb_tail);
+ }
+ }
+
+ // If we are looking at a 64bit argument, we need to also check the bits
+ // in the MSB half of the system call argument.
+ if (cond.width_ == ErrorCode::TP_64BIT) {
+ uint32_t msb_bits = static_cast<uint32_t>(cond.value_ >> 32);
+ if (!msb_bits) {
+ // No bits are set in the MSB half. The test will always fail.
+ msb_head = lsb_head;
+ } else {
+ msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
+ msb_bits,
+ RetExpression(gen, *cond.passed_),
+ lsb_head);
+ gen->JoinInstructions(msb_head, msb_tail);
+ }
+ }
+ break;
default:
- // TODO(markus): We can only check for equality so far.
+ // TODO(markus): Need to add support for OP_GREATER
SANDBOX_DIE("Not implemented");
break;
}
@@ -785,8 +897,6 @@ Instruction *Sandbox::CondExpression(CodeGen *gen, const ErrorCode& cond) {
gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, 0,
lsb_head,
invalid_64bit));
- } else {
- gen->JoinInstructions(msb_tail, lsb_head);
}
return msb_head;
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
index aee03fa..1142ce5 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.h
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -97,6 +97,13 @@
#define IPC_64 0x0100
#endif
+#ifndef BPF_MOD
+#define BPF_MOD 0x90
+#endif
+#ifndef BPF_XOR
+#define BPF_XOR 0xA0
+#endif
+
// In order to build will older tool chains, we currently have to avoid
// including <linux/seccomp.h>. Until that can be fixed (if ever). Rely on
// our own definitions of the seccomp kernel ABI.
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
index 90ec8ff..015e2d2 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <errno.h>
+#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/utsname.h>
@@ -16,7 +18,10 @@
#include "testing/gtest/include/gtest/gtest.h"
// Workaround for Android's prctl.h file.
-#if !defined(PR_CAPBSET_READ)
+#ifndef PR_GET_ENDIAN
+#define PR_GET_ENDIAN 19
+#endif
+#ifndef PR_CAPBSET_READ
#define PR_CAPBSET_READ 23
#define PR_CAPBSET_DROP 24
#endif
@@ -180,6 +185,51 @@ BPF_TEST(SandboxBpf, BasicBlacklistWithSigsys,
BPF_ASSERT(BPF_AUX == kExpectedReturnValue);
}
+// A simple test that verifies we can return arbitrary errno values.
+
+ErrorCode ErrnoTestPolicy(int sysno, void *) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ }
+
+ switch (sysno) {
+ case __NR_dup2:
+ // Pretend that dup2() worked, but don't actually do anything.
+ return ErrorCode(0);
+ case __NR_setuid:
+ // Return errno = 1
+ return ErrorCode(1);
+ case __NR_setgid:
+ // Return maximum errno value (typically 4095).
+ return ErrorCode(ErrorCode::ERR_MAX_ERRNO);
+ default:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+BPF_TEST(SandboxBpf, ErrnoTest, ErrnoTestPolicy) {
+ // Verify that dup2() returns success, but doesn't actually run.
+ int fds[4];
+ BPF_ASSERT(pipe(fds) == 0);
+ BPF_ASSERT(pipe(fds+2) == 0);
+ BPF_ASSERT(dup2(fds[2], fds[0]) == 0);
+ char buf[1] = { };
+ BPF_ASSERT(write(fds[1], "\x55", 1) == 1);
+ BPF_ASSERT(write(fds[3], "\xAA", 1) == 1);
+ BPF_ASSERT(read(fds[0], buf, 1) == 1);
+
+ // If dup2() executed, we will read \xAA, but it dup2() has been turned
+ // into a no-op by our policy, then we will read \x55.
+ BPF_ASSERT(buf[0] == '\x55');
+
+ // Verify that we can return the minimum and maximum errno values.
+ BPF_ASSERT(setuid(0) == -1);
+ BPF_ASSERT(errno == 1);
+ BPF_ASSERT(setgid(0) == -1);
+ BPF_ASSERT(errno == ErrorCode::ERR_MAX_ERRNO);
+}
+
// A more complex, but synthetic policy. This tests the correctness of the BPF
// program by iterating through all syscalls and checking for an errno that
// depends on the syscall number. Unlike the Verifier, this exercises the BPF
@@ -590,6 +640,54 @@ BPF_TEST(SandboxBpf, UseOpenBroker, DenyOpenPolicy,
BPF_ASSERT(read(cpu_info_fd, buf, sizeof(buf)) > 0);
}
+// Simple test demonstrating how to use Sandbox::Cond()
+
+ErrorCode SimpleCondTestPolicy(int sysno, void *) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ }
+
+ // We deliberately return unusual errno values upon failure, so that we
+ // can uniquely test for these values. In a "real" policy, you would want
+ // to return more traditional values.
+ switch (sysno) {
+ case __NR_open:
+ // Allow opening files for reading, but don't allow writing.
+ COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_be_all_zero_bits);
+ return Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ O_ACCMODE /* 0x3 */,
+ ErrorCode(EROFS),
+ ErrorCode(ErrorCode::ERR_ALLOWED));
+ case __NR_prctl:
+ // Allow prctl(PR_SET_DUMPABLE) and prctl(PR_GET_DUMPABLE), but
+ // disallow everything else.
+ return Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ PR_SET_DUMPABLE,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ PR_GET_DUMPABLE,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ ErrorCode(ENOMEM)));
+ default:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+BPF_TEST(SandboxBpf, SimpleCondTest, SimpleCondTestPolicy) {
+ int fd;
+ BPF_ASSERT((fd = open("/proc/self/comm", O_RDWR)) == -1);
+ BPF_ASSERT(errno == EROFS);
+ BPF_ASSERT((fd = open("/proc/self/comm", O_RDONLY)) >= 0);
+ close(fd);
+
+ int ret;
+ BPF_ASSERT((ret = prctl(PR_GET_DUMPABLE)) >= 0);
+ BPF_ASSERT(prctl(PR_SET_DUMPABLE, 1-ret) == 0);
+ BPF_ASSERT(prctl(PR_GET_ENDIAN, &ret) == -1);
+ BPF_ASSERT(errno == ENOMEM);
+}
+
// This test exercises the Sandbox::Cond() method by building a complex
// tree of conditional equality operations. It then makes system calls and
// verifies that they return the values that we expected from our BPF
@@ -908,7 +1006,7 @@ ErrorCode EqualityArgumentWidthPolicy(int sysno, void *) {
Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
0x55555555, ErrorCode(1), ErrorCode(2)),
Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_EQUAL,
- 0x55555555AAAAAAAAull, ErrorCode(1), ErrorCode(2)));
+ 0x55555555AAAAAAAAULL, ErrorCode(1), ErrorCode(2)));
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
@@ -921,10 +1019,10 @@ BPF_TEST(SandboxBpf, EqualityArgumentWidth, EqualityArgumentWidthPolicy) {
// On 32bit machines, there is no way to pass a 64bit argument through the
// syscall interface. So, we have to skip the part of the test that requires
// 64bit arguments.
- BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x55555555AAAAAAAAull) == -1);
- BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x5555555500000000ull) == -2);
- BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x5555555511111111ull) == -2);
- BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x11111111AAAAAAAAull) == -2);
+ BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x55555555AAAAAAAAULL) == -1);
+ BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x5555555500000000ULL) == -2);
+ BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x5555555511111111ULL) == -2);
+ BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x11111111AAAAAAAAULL) == -2);
#endif
}
@@ -935,7 +1033,7 @@ BPF_TEST(SandboxBpf, EqualityArgumentWidth, EqualityArgumentWidthPolicy) {
BPF_DEATH_TEST(SandboxBpf, EqualityArgumentUnallowed64bit,
DEATH_MESSAGE("Unexpected 64bit argument detected"),
EqualityArgumentWidthPolicy) {
- SandboxSyscall(__NR_uname, 0, 0x5555555555555555ull);
+ SandboxSyscall(__NR_uname, 0, 0x5555555555555555ULL);
}
#endif
@@ -955,7 +1053,7 @@ BPF_TEST(SandboxBpf, EqualityWithNegativeArguments,
EqualityWithNegativeArgumentsPolicy) {
BPF_ASSERT(SandboxSyscall(__NR_uname, 0xFFFFFFFF) == -1);
BPF_ASSERT(SandboxSyscall(__NR_uname, -1) == -1);
- BPF_ASSERT(SandboxSyscall(__NR_uname, -1ll) == -1);
+ BPF_ASSERT(SandboxSyscall(__NR_uname, -1LL) == -1);
}
#if __SIZEOF_POINTER__ > 4
@@ -965,8 +1063,370 @@ BPF_DEATH_TEST(SandboxBpf, EqualityWithNegative64bitArguments,
// When expecting a 32bit system call argument, we look at the MSB of the
// 64bit value and allow both "0" and "-1". But the latter is allowed only
// iff the LSB was negative. So, this death test should error out.
- BPF_ASSERT(SandboxSyscall(__NR_uname, 0xFFFFFFFF00000000ll) == -1);
+ BPF_ASSERT(SandboxSyscall(__NR_uname, 0xFFFFFFFF00000000LL) == -1);
}
#endif
+ErrorCode AllBitTestPolicy(int sysno, void *) {
+ // Test the OP_HAS_ALL_BITS conditional test operator with a couple of
+ // different bitmasks. We try to find bitmasks that could conceivably
+ // touch corner cases.
+ // For all of these tests, we override the uname(). We can make use with
+ // a single system call number, as we use the first system call argument to
+ // select the different bit masks that we want to test against.
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ } else if (sysno == __NR_uname) {
+ return Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+ Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x0,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 1,
+ Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x1,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 2,
+ Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x3,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 3,
+ Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x80000000,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 4,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x0,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 5,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x1,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 6,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x3,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 7,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x80000000,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 8,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x100000000ULL,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 9,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x300000000ULL,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 10,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x100000001ULL,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Kill("Invalid test case number"))))))))))));
+
+ } else {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+BPF_TEST(SandboxBpf, AllBitTests, AllBitTestPolicy) {
+ // Our uname() system call returns ErrorCode(1) for success and
+ // ErrorCode(0) for failure. SandboxSyscall() turns this into an
+ // exit code of -1 or 0. This is compatible with traditional
+ // C-style boolean test values.
+
+ // 32bit test: all of 0x0 (should always be true)
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 0, 0));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 0, 1));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 0, 3));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 0, 0xFFFFFFFFU));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 0, -1LL));
+
+ // 32bit test: all of 0x1
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 1, 0));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 1, 1));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 1, 2));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 1, 3));
+
+ // 32bit test: all of 0x3
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 2, 0));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 2, 1));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 2, 2));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 2, 3));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 2, 7));
+
+ // 32bit test: all of 0x80000000
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 3, 0));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 3, 0x40000000U));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 3, 0x80000000U));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 3, 0xC0000000U));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 3,-0x80000000LL));
+
+ // 64bit test: all of 0x0 (should always be true)
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 4, 0));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 4, 1));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 4, 3));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 4, 0xFFFFFFFFU));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 4, 0x100000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 4, 0x300000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 4, 0x8000000000000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 4, -1LL));
+
+ // 64bit test: all of 0x1
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 5, 0));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 5, 1));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 5, 2));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 5, 3));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 5, 0x100000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 5, 0x100000001LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 5, 0x100000002LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 5, 0x100000003LL));
+
+ // 64bit test: all of 0x3
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 6, 0));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 6, 1));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 6, 2));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 6, 3));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 6, 7));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 6, 0x100000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 6, 0x100000001LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 6, 0x100000002LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 6, 0x100000003LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 6, 0x100000007LL));
+
+ // 64bit test: all of 0x80000000
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 7, 0));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 7, 0x40000000U));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 7, 0x80000000U));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 7, 0xC0000000U));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 7,-0x80000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 7, 0x100000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 7, 0x140000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 7, 0x180000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 7, 0x1C0000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 7,-0x180000000LL));
+
+ // 64bit test: all of 0x100000000
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 8, 0x000000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 8, 0x100000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 8, 0x200000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 8, 0x300000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 8, 0x000000001LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 8, 0x100000001LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 8, 0x200000001LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 8, 0x300000001LL));
+
+ // 64bit test: all of 0x300000000
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 9, 0x000000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 9, 0x100000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 9, 0x200000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 9, 0x300000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 9, 0x700000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 9, 0x000000001LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 9, 0x100000001LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 9, 0x200000001LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 9, 0x300000001LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 9, 0x700000001LL));
+
+ // 64bit test: all of 0x100000001
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 10, 0x000000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 10, 0x000000001LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 10, 0x100000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 10, 0x100000001LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 10, 0xFFFFFFFFU));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 10, -1L));
+}
+
+ErrorCode AnyBitTestPolicy(int sysno, void *) {
+ // Test the OP_HAS_ANY_BITS conditional test operator with a couple of
+ // different bitmasks. We try to find bitmasks that could conceivably
+ // touch corner cases.
+ // For all of these tests, we override the uname(). We can make use with
+ // a single system call number, as we use the first system call argument to
+ // select the different bit masks that we want to test against.
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ } else if (sysno == __NR_uname) {
+ return Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+ Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x0,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 1,
+ Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x1,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 2,
+ Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x3,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 3,
+ Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x80000000,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 4,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x0,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 5,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x1,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 6,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x3,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 7,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x80000000,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 8,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x100000000ULL,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 9,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x300000000ULL,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 10,
+ Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x100000001ULL,
+ ErrorCode(1), ErrorCode(0)),
+
+ Sandbox::Kill("Invalid test case number"))))))))))));
+
+ } else {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+BPF_TEST(SandboxBpf, AnyBitTests, AnyBitTestPolicy) {
+ // Our uname() system call returns ErrorCode(1) for success and
+ // ErrorCode(0) for failure. SandboxSyscall() turns this into an
+ // exit code of -1 or 0. This is compatible with traditional
+ // C-style boolean test values.
+
+ // 32bit test: any of 0x0 (should always be false)
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 0, 0));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 0, 1));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 0, 3));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 0, 0xFFFFFFFFU));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 0, -1LL));
+
+ // 32bit test: any of 0x1
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 1, 0));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 1, 1));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 1, 2));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 1, 3));
+
+ // 32bit test: any of 0x3
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 2, 0));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 2, 1));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 2, 2));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 2, 3));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 2, 7));
+
+ // 32bit test: any of 0x80000000
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 3, 0));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 3, 0x40000000U));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 3, 0x80000000U));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 3, 0xC0000000U));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 3,-0x80000000LL));
+
+ // 64bit test: any of 0x0 (should always be false)
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 4, 0));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 4, 1));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 4, 3));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 4, 0xFFFFFFFFU));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 4, 0x100000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 4, 0x300000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 4, 0x8000000000000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 4, -1LL));
+
+ // 64bit test: any of 0x1
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 5, 0));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 5, 1));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 5, 2));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 5, 3));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 5, 0x100000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 5, 0x100000001LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 5, 0x100000002LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 5, 0x100000003LL));
+
+ // 64bit test: any of 0x3
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 6, 0));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 6, 1));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 6, 2));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 6, 3));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 6, 7));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 6, 0x100000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 6, 0x100000001LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 6, 0x100000002LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 6, 0x100000003LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 6, 0x100000007LL));
+
+ // 64bit test: any of 0x80000000
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 7, 0));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 7, 0x40000000U));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 7, 0x80000000U));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 7, 0xC0000000U));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 7,-0x80000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 7, 0x100000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 7, 0x140000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 7, 0x180000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 7, 0x1C0000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 7,-0x180000000LL));
+
+ // 64bit test: any of 0x100000000
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 8, 0x000000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 8, 0x100000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 8, 0x200000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 8, 0x300000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 8, 0x000000001LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 8, 0x100000001LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 8, 0x200000001LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 8, 0x300000001LL));
+
+ // 64bit test: any of 0x300000000
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 9, 0x000000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 9, 0x100000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 9, 0x200000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 9, 0x300000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 9, 0x700000000LL));
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 9, 0x000000001LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 9, 0x100000001LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 9, 0x200000001LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 9, 0x300000001LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 9, 0x700000001LL));
+
+ // 64bit test: any of 0x100000001
+ BPF_ASSERT(!SandboxSyscall(__NR_uname, 10, 0x000000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 10, 0x000000001LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 10, 0x100000000LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 10, 0x100000001LL));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 10, 0xFFFFFFFFU));
+ BPF_ASSERT( SandboxSyscall(__NR_uname, 10, -1L));
+}
+
} // namespace
diff --git a/sandbox/linux/seccomp-bpf/verifier.cc b/sandbox/linux/seccomp-bpf/verifier.cc
index 1c99619..0403e1b 100644
--- a/sandbox/linux/seccomp-bpf/verifier.cc
+++ b/sandbox/linux/seccomp-bpf/verifier.cc
@@ -112,7 +112,41 @@ bool Verifier::VerifyErrorCode(const std::vector<struct sock_filter>& program,
}
}
break;
- default: // TODO(markus): We can only check for equality so far.
+ case ErrorCode::OP_HAS_ALL_BITS:
+ case ErrorCode::OP_HAS_ANY_BITS:
+ // A comprehensive test of bit values is difficult and potentially rather
+ // time-expensive. We avoid doing so at run-time and instead rely on the
+ // unittest for full testing. The test that we have here covers just the
+ // common cases. We test against the bitmask itself, all zeros and all
+ // ones.
+ {
+ // Testing "any" bits against a zero mask is always false. So, there
+ // are some cases, where we expect tests to take the "failed_" branch
+ // even though this is a test that normally should take "passed_".
+ const ErrorCode& passed =
+ !code.value_ && code.op_ == ErrorCode::OP_HAS_ANY_BITS
+ ? *code.failed_ : *code.passed_;
+ // Similary, testing for "all" bits in a zero mask is always true. So,
+ // some cases pass despite them normally failing.
+ const ErrorCode& failed =
+ !code.value_ && code.op_ == ErrorCode::OP_HAS_ALL_BITS
+ ? *code.passed_ : *code.failed_;
+
+ data->args[code.argno_] = code.value_;
+ if (!VerifyErrorCode(program, data, passed, err)) {
+ return false;
+ }
+ data->args[code.argno_] = -1LL;
+ if (!VerifyErrorCode(program, data, passed, err)) {
+ return false;
+ }
+ data->args[code.argno_] = 0;
+ if (!VerifyErrorCode(program, data, failed, err)) {
+ return false;
+ }
+ }
+ break;
+ default: // TODO(markus): Need to add support for OP_GREATER
*err = "Unsupported operation in conditional error code";
return false;
}
@@ -159,6 +193,9 @@ uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
return 0;
}
return r; }
+ case BPF_ALU:
+ Alu(&state, insn, err);
+ break;
default:
*err = "Unexpected instruction in BPF program";
break;
@@ -248,4 +285,69 @@ uint32_t Verifier::Ret(State *, const struct sock_filter& insn,
return insn.k;
}
+void Verifier::Alu(State *state, const struct sock_filter& insn,
+ const char **err) {
+ if (BPF_OP(insn.code) == BPF_NEG) {
+ state->accumulator = -state->accumulator;
+ return;
+ } else {
+ if (BPF_SRC(insn.code) != BPF_K) {
+ *err = "Unexpected source operand in arithmetic operation";
+ return;
+ }
+ switch (BPF_OP(insn.code)) {
+ case BPF_ADD:
+ state->accumulator += insn.k;
+ break;
+ case BPF_SUB:
+ state->accumulator -= insn.k;
+ break;
+ case BPF_MUL:
+ state->accumulator *= insn.k;
+ break;
+ case BPF_DIV:
+ if (!insn.k) {
+ *err = "Illegal division by zero";
+ break;
+ }
+ state->accumulator /= insn.k;
+ break;
+ case BPF_MOD:
+ if (!insn.k) {
+ *err = "Illegal division by zero";
+ break;
+ }
+ state->accumulator %= insn.k;
+ break;
+ case BPF_OR:
+ state->accumulator |= insn.k;
+ break;
+ case BPF_XOR:
+ state->accumulator ^= insn.k;
+ break;
+ case BPF_AND:
+ state->accumulator &= insn.k;
+ break;
+ case BPF_LSH:
+ if (insn.k > 32) {
+ *err = "Illegal shift operation";
+ break;
+ }
+ state->accumulator <<= insn.k;
+ break;
+ case BPF_RSH:
+ if (insn.k > 32) {
+ *err = "Illegal shift operation";
+ break;
+ }
+ state->accumulator >>= insn.k;
+ break;
+ default:
+ *err = "Invalid operator in arithmetic operation";
+ break;
+ }
+ }
+}
+
+
} // namespace
diff --git a/sandbox/linux/seccomp-bpf/verifier.h b/sandbox/linux/seccomp-bpf/verifier.h
index f66e7e93..26a3156 100644
--- a/sandbox/linux/seccomp-bpf/verifier.h
+++ b/sandbox/linux/seccomp-bpf/verifier.h
@@ -69,6 +69,8 @@ class Verifier {
const char **err);
static uint32_t Ret(State *state, const struct sock_filter& insn,
const char **err);
+ static void Alu(State *state, const struct sock_filter& insn,
+ const char **err);
DISALLOW_IMPLICIT_CONSTRUCTORS(Verifier);
};