diff options
author | markus@chromium.org <markus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-20 11:50:50 +0000 |
---|---|---|
committer | markus@chromium.org <markus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-20 11:50:50 +0000 |
commit | a301233667339ec1a231c5cf60fb58c2e80adf90 (patch) | |
tree | af2dc00574e90a72cadbe71b6fd91a4e73290a3b /sandbox | |
parent | 91c92c8a01c1f376f0ce8881fd1d800f2aa1ca1e (diff) | |
download | chromium_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.cc | 15 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/errorcode.h | 47 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/sandbox_bpf.cc | 118 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/sandbox_bpf.h | 7 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc | 478 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/verifier.cc | 104 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/verifier.h | 2 |
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); }; |