Chromium Code Reviews| 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()]) |
|
Robert Sesek
2013/11/08 21:14:33
For another CL: nested ternary is unreadable.
jln (very slow on Chromium)
2013/11/08 21:22:37
Agreed!
|
| + : 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; |