// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "sandbox/linux/bpf_dsl/verifier.h" #include #include #include "base/macros.h" #include "sandbox/linux/bpf_dsl/seccomp_macros.h" #include "sandbox/linux/bpf_dsl/trap_registry.h" #include "sandbox/linux/system_headers/linux_filter.h" #include "sandbox/linux/system_headers/linux_seccomp.h" namespace sandbox { namespace bpf_dsl { namespace { struct State { State(const std::vector& p, const struct arch_seccomp_data& d) : program(p), data(d), ip(0), accumulator(0), acc_is_valid(false) {} const std::vector& program; const struct arch_seccomp_data& data; unsigned int ip; uint32_t accumulator; bool acc_is_valid; private: DISALLOW_IMPLICIT_CONSTRUCTORS(State); }; 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 || insn.jt != 0 || insn.jf != 0) { *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(&state->data) + insn.k, 4); } else { *err = "Invalid operand in BPF_LD instruction"; return; } state->acc_is_valid = true; return; } 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) { compilation_failure: *err = "Invalid BPF_JMP instruction"; return; } state->ip += insn.k; } else { 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; } } } 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; } return insn.k; } void 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 uint32_t Verifier::EvaluateBPF(const std::vector& program, const struct arch_seccomp_data& data, const char** err) { *err = NULL; if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) { *err = "Invalid program length"; return 0; } for (State state(program, data); !*err; ++state.ip) { if (state.ip >= program.size()) { *err = "Invalid instruction pointer in BPF program"; break; } 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_ALLOW: case SECCOMP_RET_ERRNO: case SECCOMP_RET_KILL: case SECCOMP_RET_TRACE: case SECCOMP_RET_TRAP: break; case SECCOMP_RET_INVALID: // Should never show up in BPF program default: *err = "Unexpected return code found in BPF program"; return 0; } return r; } case BPF_ALU: Alu(&state, insn, err); break; default: *err = "Unexpected instruction in BPF program"; break; } } return 0; } } // namespace bpf_dsl } // namespace sandbox