| Index: sandbox/linux/seccomp-bpf/verifier.cc
|
| diff --git a/sandbox/linux/seccomp-bpf/verifier.cc b/sandbox/linux/seccomp-bpf/verifier.cc
|
| index 798aced348dadfc90abf05f641e00e5f52dc3901..1d6b26d59254d4858813113632dd44664fdeb581 100644
|
| --- a/sandbox/linux/seccomp-bpf/verifier.cc
|
| +++ b/sandbox/linux/seccomp-bpf/verifier.cc
|
| @@ -9,7 +9,6 @@
|
| #include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
|
| #include "sandbox/linux/seccomp-bpf/verifier.h"
|
|
|
| -
|
| namespace {
|
|
|
| using playground2::ErrorCode;
|
| @@ -19,24 +18,20 @@ using playground2::arch_seccomp_data;
|
|
|
| struct State {
|
| State(const std::vector<struct sock_filter>& p,
|
| - const struct arch_seccomp_data& d) :
|
| - program(p),
|
| - data(d),
|
| - ip(0),
|
| - accumulator(0),
|
| - acc_is_valid(false) {
|
| - }
|
| + const struct arch_seccomp_data& d)
|
| + : program(p), data(d), ip(0), accumulator(0), acc_is_valid(false) {}
|
| const std::vector<struct sock_filter>& program;
|
| - const struct arch_seccomp_data& data;
|
| - unsigned int ip;
|
| - uint32_t accumulator;
|
| - bool acc_is_valid;
|
| + const struct arch_seccomp_data& data;
|
| + unsigned int ip;
|
| + uint32_t accumulator;
|
| + bool acc_is_valid;
|
|
|
| private:
|
| DISALLOW_IMPLICIT_CONSTRUCTORS(State);
|
| };
|
|
|
| -uint32_t EvaluateErrorCode(Sandbox *sandbox, const ErrorCode& code,
|
| +uint32_t EvaluateErrorCode(Sandbox* sandbox,
|
| + const ErrorCode& code,
|
| const struct arch_seccomp_data& data) {
|
| if (code.error_type() == ErrorCode::ET_SIMPLE ||
|
| code.error_type() == ErrorCode::ET_TRAP) {
|
| @@ -45,49 +40,50 @@ uint32_t EvaluateErrorCode(Sandbox *sandbox, const ErrorCode& code,
|
| if (code.width() == ErrorCode::TP_32BIT &&
|
| (data.args[code.argno()] >> 32) &&
|
| (data.args[code.argno()] & 0xFFFFFFFF80000000ull) !=
|
| - 0xFFFFFFFF80000000ull) {
|
| + 0xFFFFFFFF80000000ull) {
|
| return sandbox->Unexpected64bitArgument().err();
|
| }
|
| switch (code.op()) {
|
| - case ErrorCode::OP_EQUAL:
|
| - return EvaluateErrorCode(sandbox,
|
| - (code.width() == ErrorCode::TP_32BIT
|
| - ? uint32_t(data.args[code.argno()])
|
| - : data.args[code.argno()]) == code.value()
|
| - ? *code.passed()
|
| - : *code.failed(),
|
| - data);
|
| - case ErrorCode::OP_HAS_ALL_BITS:
|
| - return EvaluateErrorCode(sandbox,
|
| - ((code.width() == ErrorCode::TP_32BIT
|
| - ? uint32_t(data.args[code.argno()])
|
| - : data.args[code.argno()]) & code.value())
|
| - == code.value()
|
| - ? *code.passed()
|
| - : *code.failed(),
|
| - data);
|
| - case ErrorCode::OP_HAS_ANY_BITS:
|
| - return EvaluateErrorCode(sandbox,
|
| - (code.width() == ErrorCode::TP_32BIT
|
| - ? uint32_t(data.args[code.argno()])
|
| - : data.args[code.argno()]) & code.value()
|
| - ? *code.passed()
|
| - : *code.failed(),
|
| - data);
|
| - default:
|
| - return SECCOMP_RET_INVALID;
|
| + case ErrorCode::OP_EQUAL:
|
| + return EvaluateErrorCode(sandbox,
|
| + (code.width() == ErrorCode::TP_32BIT
|
| + ? uint32_t(data.args[code.argno()])
|
| + : data.args[code.argno()]) == code.value()
|
| + ? *code.passed()
|
| + : *code.failed(),
|
| + data);
|
| + case ErrorCode::OP_HAS_ALL_BITS:
|
| + return EvaluateErrorCode(sandbox,
|
| + ((code.width() == ErrorCode::TP_32BIT
|
| + ? uint32_t(data.args[code.argno()])
|
| + : data.args[code.argno()]) &
|
| + code.value()) == code.value()
|
| + ? *code.passed()
|
| + : *code.failed(),
|
| + data);
|
| + case ErrorCode::OP_HAS_ANY_BITS:
|
| + return EvaluateErrorCode(sandbox,
|
| + (code.width() == ErrorCode::TP_32BIT
|
| + ? uint32_t(data.args[code.argno()])
|
| + : data.args[code.argno()]) &
|
| + code.value()
|
| + ? *code.passed()
|
| + : *code.failed(),
|
| + data);
|
| + default:
|
| + return SECCOMP_RET_INVALID;
|
| }
|
| } else {
|
| return SECCOMP_RET_INVALID;
|
| }
|
| }
|
|
|
| -bool VerifyErrorCode(Sandbox *sandbox,
|
| +bool VerifyErrorCode(Sandbox* sandbox,
|
| const std::vector<struct sock_filter>& program,
|
| - struct arch_seccomp_data *data,
|
| + struct arch_seccomp_data* data,
|
| const ErrorCode& root_code,
|
| const ErrorCode& code,
|
| - const char **err) {
|
| + const char** err) {
|
| if (code.error_type() == ErrorCode::ET_SIMPLE ||
|
| code.error_type() == ErrorCode::ET_TRAP) {
|
| uint32_t computed_ret = Verifier::EvaluateBPF(program, *data, err);
|
| @@ -110,102 +106,113 @@ bool VerifyErrorCode(Sandbox *sandbox,
|
| return false;
|
| }
|
| switch (code.op()) {
|
| - case ErrorCode::OP_EQUAL:
|
| - // Verify that we can check a 32bit value (or the LSB of a 64bit value)
|
| - // for equality.
|
| - data->args[code.argno()] = code.value();
|
| - if (!VerifyErrorCode(sandbox, program, data, root_code,
|
| - *code.passed(), err)) {
|
| - return false;
|
| - }
|
| -
|
| - // Change the value to no longer match and verify that this is detected
|
| - // as an inequality.
|
| - data->args[code.argno()] = code.value() ^ 0x55AA55AA;
|
| - if (!VerifyErrorCode(sandbox, program, data, root_code,
|
| - *code.failed(), err)) {
|
| - return false;
|
| - }
|
| -
|
| - // BPF programs can only ever operate on 32bit values. So, we have
|
| - // generated additional BPF instructions that inspect the MSB. Verify
|
| - // that they behave as intended.
|
| - if (code.width() == ErrorCode::TP_32BIT) {
|
| - if (code.value() >> 32) {
|
| - SANDBOX_DIE("Invalid comparison of a 32bit system call argument "
|
| - "against a 64bit constant; this test is always false.");
|
| - }
|
| -
|
| - // If the system call argument was intended to be a 32bit parameter,
|
| - // verify that it is a fatal error if a 64bit value is ever passed
|
| - // here.
|
| - data->args[code.argno()] = 0x100000000ull;
|
| - if (!VerifyErrorCode(sandbox, program, data, root_code,
|
| - sandbox->Unexpected64bitArgument(),
|
| - err)) {
|
| + case ErrorCode::OP_EQUAL:
|
| + // Verify that we can check a 32bit value (or the LSB of a 64bit value)
|
| + // for equality.
|
| + data->args[code.argno()] = code.value();
|
| + if (!VerifyErrorCode(
|
| + sandbox, program, data, root_code, *code.passed(), err)) {
|
| return false;
|
| }
|
| - } else {
|
| - // If the system call argument was intended to be a 64bit parameter,
|
| - // verify that we can handle (in-)equality for the MSB. This is
|
| - // essentially the same test that we did earlier for the LSB.
|
| - // We only need to verify the behavior of the inequality test. We
|
| - // know that the equality test already passed, as unlike the kernel
|
| - // the Verifier does operate on 64bit quantities.
|
| - data->args[code.argno()] = code.value() ^ 0x55AA55AA00000000ull;
|
| - if (!VerifyErrorCode(sandbox, program, data, root_code,
|
| - *code.failed(), err)) {
|
| +
|
| + // Change the value to no longer match and verify that this is detected
|
| + // as an inequality.
|
| + data->args[code.argno()] = code.value() ^ 0x55AA55AA;
|
| + if (!VerifyErrorCode(
|
| + sandbox, program, data, root_code, *code.failed(), err)) {
|
| return false;
|
| }
|
| - }
|
| - break;
|
| - 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) ||
|
|
|
| - // On a 32bit system, it is impossible to pass a 64bit value as a
|
| - // system call argument. So, some additional tests always evaluate
|
| - // as false.
|
| - ((code.value() & ~uint64_t(uintptr_t(-1))) &&
|
| - code.op() == ErrorCode::OP_HAS_ALL_BITS) ||
|
| - (code.value() && !(code.value() & uintptr_t(-1)) &&
|
| - code.op() == ErrorCode::OP_HAS_ANY_BITS)
|
| + // BPF programs can only ever operate on 32bit values. So, we have
|
| + // generated additional BPF instructions that inspect the MSB. Verify
|
| + // that they behave as intended.
|
| + if (code.width() == ErrorCode::TP_32BIT) {
|
| + if (code.value() >> 32) {
|
| + SANDBOX_DIE(
|
| + "Invalid comparison of a 32bit system call argument "
|
| + "against a 64bit constant; this test is always false.");
|
| + }
|
|
|
| - ? *code.failed() : *code.passed();
|
| + // If the system call argument was intended to be a 32bit parameter,
|
| + // verify that it is a fatal error if a 64bit value is ever passed
|
| + // here.
|
| + data->args[code.argno()] = 0x100000000ull;
|
| + if (!VerifyErrorCode(sandbox,
|
| + program,
|
| + data,
|
| + root_code,
|
| + sandbox->Unexpected64bitArgument(),
|
| + err)) {
|
| + return false;
|
| + }
|
| + } else {
|
| + // If the system call argument was intended to be a 64bit parameter,
|
| + // verify that we can handle (in-)equality for the MSB. This is
|
| + // essentially the same test that we did earlier for the LSB.
|
| + // We only need to verify the behavior of the inequality test. We
|
| + // know that the equality test already passed, as unlike the kernel
|
| + // the Verifier does operate on 64bit quantities.
|
| + data->args[code.argno()] = code.value() ^ 0x55AA55AA00000000ull;
|
| + if (!VerifyErrorCode(
|
| + sandbox, program, data, root_code, *code.failed(), err)) {
|
| + return false;
|
| + }
|
| + }
|
| + break;
|
| + 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) ||
|
|
|
| - // 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();
|
| + // On a 32bit system, it is impossible to pass a 64bit
|
| + // value as a
|
| + // system call argument. So, some additional tests always
|
| + // evaluate
|
| + // as false.
|
| + ((code.value() & ~uint64_t(uintptr_t(-1))) &&
|
| + code.op() == ErrorCode::OP_HAS_ALL_BITS) ||
|
| + (code.value() && !(code.value() & uintptr_t(-1)) &&
|
| + code.op() == ErrorCode::OP_HAS_ANY_BITS)
|
| + ? *code.failed()
|
| + : *code.passed();
|
|
|
| - data->args[code.argno()] = code.value() & uintptr_t(-1);
|
| - if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) {
|
| - return false;
|
| - }
|
| - data->args[code.argno()] = uintptr_t(-1);
|
| - if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) {
|
| - return false;
|
| - }
|
| - data->args[code.argno()] = 0;
|
| - if (!VerifyErrorCode(sandbox, program, data, root_code, failed, err)) {
|
| - return false;
|
| + // 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() & uintptr_t(-1);
|
| + if (!VerifyErrorCode(
|
| + sandbox, program, data, root_code, passed, err)) {
|
| + return false;
|
| + }
|
| + data->args[code.argno()] = uintptr_t(-1);
|
| + if (!VerifyErrorCode(
|
| + sandbox, program, data, root_code, passed, err)) {
|
| + return false;
|
| + }
|
| + data->args[code.argno()] = 0;
|
| + if (!VerifyErrorCode(
|
| + sandbox, program, data, root_code, failed, err)) {
|
| + return false;
|
| + }
|
| }
|
| - }
|
| - break;
|
| - default: // TODO(markus): Need to add support for OP_GREATER
|
| - *err = "Unsupported operation in conditional error code";
|
| - return false;
|
| + break;
|
| + default: // TODO(markus): Need to add support for OP_GREATER
|
| + *err = "Unsupported operation in conditional error code";
|
| + return false;
|
| }
|
| } else {
|
| *err = "Attempting to return invalid error code from BPF program";
|
| @@ -214,16 +221,15 @@ bool VerifyErrorCode(Sandbox *sandbox,
|
| return true;
|
| }
|
|
|
| -void Ld(State *state, const struct sock_filter& insn, const char **err) {
|
| - if (BPF_SIZE(insn.code) != BPF_W ||
|
| - BPF_MODE(insn.code) != BPF_ABS) {
|
| +void Ld(State* state, const struct sock_filter& insn, const char** err) {
|
| + if (BPF_SIZE(insn.code) != BPF_W || BPF_MODE(insn.code) != BPF_ABS) {
|
| *err = "Invalid BPF_LD instruction";
|
| return;
|
| }
|
| if (insn.k < sizeof(struct arch_seccomp_data) && (insn.k & 3) == 0) {
|
| // We only allow loading of properly aligned 32bit quantities.
|
| memcpy(&state->accumulator,
|
| - reinterpret_cast<const char *>(&state->data) + insn.k,
|
| + reinterpret_cast<const char*>(&state->data) + insn.k,
|
| 4);
|
| } else {
|
| *err = "Invalid operand in BPF_LD instruction";
|
| @@ -233,7 +239,7 @@ void Ld(State *state, const struct sock_filter& insn, const char **err) {
|
| return;
|
| }
|
|
|
| -void Jmp(State *state, const struct sock_filter& insn, const char **err) {
|
| +void Jmp(State* state, const struct sock_filter& insn, const char** err) {
|
| if (BPF_OP(insn.code) == BPF_JA) {
|
| if (state->ip + insn.k + 1 >= state->program.size() ||
|
| state->ip + insn.k + 1 <= state->ip) {
|
| @@ -243,48 +249,47 @@ void Jmp(State *state, const struct sock_filter& insn, const char **err) {
|
| }
|
| state->ip += insn.k;
|
| } else {
|
| - if (BPF_SRC(insn.code) != BPF_K ||
|
| - !state->acc_is_valid ||
|
| + if (BPF_SRC(insn.code) != BPF_K || !state->acc_is_valid ||
|
| state->ip + insn.jt + 1 >= state->program.size() ||
|
| state->ip + insn.jf + 1 >= state->program.size()) {
|
| goto compilation_failure;
|
| }
|
| switch (BPF_OP(insn.code)) {
|
| - case BPF_JEQ:
|
| - if (state->accumulator == insn.k) {
|
| - state->ip += insn.jt;
|
| - } else {
|
| - state->ip += insn.jf;
|
| - }
|
| - break;
|
| - case BPF_JGT:
|
| - if (state->accumulator > insn.k) {
|
| - state->ip += insn.jt;
|
| - } else {
|
| - state->ip += insn.jf;
|
| - }
|
| - break;
|
| - case BPF_JGE:
|
| - if (state->accumulator >= insn.k) {
|
| - state->ip += insn.jt;
|
| - } else {
|
| - state->ip += insn.jf;
|
| - }
|
| - break;
|
| - case BPF_JSET:
|
| - if (state->accumulator & insn.k) {
|
| - state->ip += insn.jt;
|
| - } else {
|
| - state->ip += insn.jf;
|
| - }
|
| - break;
|
| - default:
|
| - goto compilation_failure;
|
| + case BPF_JEQ:
|
| + if (state->accumulator == insn.k) {
|
| + state->ip += insn.jt;
|
| + } else {
|
| + state->ip += insn.jf;
|
| + }
|
| + break;
|
| + case BPF_JGT:
|
| + if (state->accumulator > insn.k) {
|
| + state->ip += insn.jt;
|
| + } else {
|
| + state->ip += insn.jf;
|
| + }
|
| + break;
|
| + case BPF_JGE:
|
| + if (state->accumulator >= insn.k) {
|
| + state->ip += insn.jt;
|
| + } else {
|
| + state->ip += insn.jf;
|
| + }
|
| + break;
|
| + case BPF_JSET:
|
| + if (state->accumulator & insn.k) {
|
| + state->ip += insn.jt;
|
| + } else {
|
| + state->ip += insn.jf;
|
| + }
|
| + break;
|
| + default:
|
| + goto compilation_failure;
|
| }
|
| }
|
| }
|
|
|
| -uint32_t Ret(State *, const struct sock_filter& insn, const char **err) {
|
| +uint32_t Ret(State*, const struct sock_filter& insn, const char** err) {
|
| if (BPF_SRC(insn.code) != BPF_K) {
|
| *err = "Invalid BPF_RET instruction";
|
| return 0;
|
| @@ -292,7 +297,7 @@ uint32_t Ret(State *, const struct sock_filter& insn, const char **err) {
|
| return insn.k;
|
| }
|
|
|
| -void Alu(State *state, const struct sock_filter& insn, const char **err) {
|
| +void Alu(State* state, const struct sock_filter& insn, const char** err) {
|
| if (BPF_OP(insn.code) == BPF_NEG) {
|
| state->accumulator = -state->accumulator;
|
| return;
|
| @@ -302,55 +307,55 @@ void Alu(State *state, const struct sock_filter& insn, const char **err) {
|
| 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";
|
| + case BPF_ADD:
|
| + state->accumulator += insn.k;
|
| break;
|
| - }
|
| - state->accumulator /= insn.k;
|
| - break;
|
| - case BPF_MOD:
|
| - if (!insn.k) {
|
| - *err = "Illegal division by zero";
|
| + case BPF_SUB:
|
| + state->accumulator -= insn.k;
|
| 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";
|
| + case BPF_MUL:
|
| + state->accumulator *= insn.k;
|
| break;
|
| - }
|
| - state->accumulator <<= insn.k;
|
| - break;
|
| - case BPF_RSH:
|
| - if (insn.k > 32) {
|
| - *err = "Illegal shift operation";
|
| + 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;
|
| - }
|
| - state->accumulator >>= insn.k;
|
| - break;
|
| - default:
|
| - *err = "Invalid operator in arithmetic operation";
|
| - break;
|
| }
|
| }
|
| }
|
| @@ -359,12 +364,12 @@ void Alu(State *state, const struct sock_filter& insn, const char **err) {
|
|
|
| namespace playground2 {
|
|
|
| -bool Verifier::VerifyBPF(Sandbox *sandbox,
|
| +bool Verifier::VerifyBPF(Sandbox* sandbox,
|
| const std::vector<struct sock_filter>& program,
|
| const SandboxBpfPolicy& policy,
|
| - const char **err) {
|
| + const char** err) {
|
| *err = NULL;
|
| - for (SyscallIterator iter(false); !iter.Done(); ) {
|
| + for (SyscallIterator iter(false); !iter.Done();) {
|
| uint32_t sysnum = iter.Next();
|
| // We ideally want to iterate over the full system call range and values
|
| // just above and just below this range. This gives us the full result set
|
| @@ -373,8 +378,8 @@ bool Verifier::VerifyBPF(Sandbox *sandbox,
|
| // indicates either i386 or x86-64; and a set bit 30 indicates x32. And
|
| // unless we pay attention to setting this bit correctly, an early check in
|
| // our BPF program will make us fail with a misleading error code.
|
| - struct arch_seccomp_data data = { static_cast<int>(sysnum),
|
| - static_cast<uint32_t>(SECCOMP_ARCH) };
|
| + struct arch_seccomp_data data = {static_cast<int>(sysnum),
|
| + static_cast<uint32_t>(SECCOMP_ARCH)};
|
| #if defined(__i386__) || defined(__x86_64__)
|
| #if defined(__x86_64__) && defined(__ILP32__)
|
| if (!(sysnum & 0x40000000u)) {
|
| @@ -396,7 +401,7 @@ bool Verifier::VerifyBPF(Sandbox *sandbox,
|
|
|
| uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
|
| const struct arch_seccomp_data& data,
|
| - const char **err) {
|
| + const char** err) {
|
| *err = NULL;
|
| if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) {
|
| *err = "Invalid program length";
|
| @@ -409,33 +414,34 @@ uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
|
| }
|
| const struct sock_filter& insn = program[state.ip];
|
| switch (BPF_CLASS(insn.code)) {
|
| - case BPF_LD:
|
| - Ld(&state, insn, err);
|
| - break;
|
| - case BPF_JMP:
|
| - Jmp(&state, insn, err);
|
| - break;
|
| - case BPF_RET: {
|
| - uint32_t r = Ret(&state, insn, err);
|
| - switch (r & SECCOMP_RET_ACTION) {
|
| - case SECCOMP_RET_TRAP:
|
| - case SECCOMP_RET_ERRNO:
|
| - case SECCOMP_RET_ALLOW:
|
| + case BPF_LD:
|
| + Ld(&state, insn, err);
|
| break;
|
| - case SECCOMP_RET_KILL: // We don't ever generate this
|
| - case SECCOMP_RET_TRACE: // We don't ever generate this
|
| - case SECCOMP_RET_INVALID: // Should never show up in BPF program
|
| - default:
|
| - *err = "Unexpected return code found in BPF program";
|
| - return 0;
|
| + case BPF_JMP:
|
| + Jmp(&state, insn, err);
|
| + break;
|
| + case BPF_RET: {
|
| + uint32_t r = Ret(&state, insn, err);
|
| + switch (r & SECCOMP_RET_ACTION) {
|
| + case SECCOMP_RET_TRAP:
|
| + case SECCOMP_RET_ERRNO:
|
| + case SECCOMP_RET_ALLOW:
|
| + break;
|
| + case SECCOMP_RET_KILL: // We don't ever generate this
|
| + case SECCOMP_RET_TRACE: // We don't ever generate this
|
| + case SECCOMP_RET_INVALID: // Should never show up in BPF program
|
| + default:
|
| + *err = "Unexpected return code found in BPF program";
|
| + return 0;
|
| + }
|
| + return r;
|
| }
|
| - return r; }
|
| - case BPF_ALU:
|
| - Alu(&state, insn, err);
|
| - break;
|
| - default:
|
| - *err = "Unexpected instruction in BPF program";
|
| - break;
|
| + case BPF_ALU:
|
| + Alu(&state, insn, err);
|
| + break;
|
| + default:
|
| + *err = "Unexpected instruction in BPF program";
|
| + break;
|
| }
|
| }
|
| return 0;
|
|
|