Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(161)

Unified Diff: sandbox/linux/seccomp-bpf/verifier.cc

Issue 1001833005: Update from https://crrev.com/320343 (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Supress Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sandbox/linux/seccomp-bpf/verifier.h ('k') | sdch/BUILD.gn » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sandbox/linux/seccomp-bpf/verifier.cc
diff --git a/sandbox/linux/seccomp-bpf/verifier.cc b/sandbox/linux/seccomp-bpf/verifier.cc
deleted file mode 100644
index e533b2d91493bad8f2baffa568ea254df425d591..0000000000000000000000000000000000000000
--- a/sandbox/linux/seccomp-bpf/verifier.cc
+++ /dev/null
@@ -1,402 +0,0 @@
-// 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/seccomp-bpf/verifier.h"
-
-#include <string.h>
-
-#include <limits>
-
-#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
-#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
-#include "sandbox/linux/bpf_dsl/policy.h"
-#include "sandbox/linux/bpf_dsl/policy_compiler.h"
-#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
-#include "sandbox/linux/bpf_dsl/syscall_set.h"
-#include "sandbox/linux/seccomp-bpf/errorcode.h"
-#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
-#include "sandbox/linux/system_headers/linux_seccomp.h"
-
-namespace sandbox {
-
-namespace {
-
-const uint64_t kLower32Bits = std::numeric_limits<uint32_t>::max();
-const uint64_t kUpper32Bits = static_cast<uint64_t>(kLower32Bits) << 32;
-const uint64_t kFull64Bits = std::numeric_limits<uint64_t>::max();
-
-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 std::vector<struct sock_filter>& program;
- const struct arch_seccomp_data& data;
- unsigned int ip;
- uint32_t accumulator;
- bool acc_is_valid;
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(State);
-};
-
-uint32_t EvaluateErrorCode(bpf_dsl::PolicyCompiler* compiler,
- const ErrorCode& code,
- const struct arch_seccomp_data& data) {
- if (code.error_type() == ErrorCode::ET_SIMPLE ||
- code.error_type() == ErrorCode::ET_TRAP) {
- return code.err();
- } else if (code.error_type() == ErrorCode::ET_COND) {
- if (code.width() == ErrorCode::TP_32BIT &&
- (data.args[code.argno()] >> 32) &&
- (data.args[code.argno()] & 0xFFFFFFFF80000000ull) !=
- 0xFFFFFFFF80000000ull) {
- return compiler->Unexpected64bitArgument().err();
- }
- bool equal = (data.args[code.argno()] & code.mask()) == code.value();
- return EvaluateErrorCode(
- compiler, equal ? *code.passed() : *code.failed(), data);
- } else {
- return SECCOMP_RET_INVALID;
- }
-}
-
-bool VerifyErrorCode(bpf_dsl::PolicyCompiler* compiler,
- const std::vector<struct sock_filter>& program,
- struct arch_seccomp_data* data,
- const ErrorCode& root_code,
- const ErrorCode& code,
- 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);
- if (*err) {
- return false;
- } else if (computed_ret != EvaluateErrorCode(compiler, root_code, *data)) {
- // For efficiency's sake, we'd much rather compare "computed_ret"
- // against "code.err()". This works most of the time, but it doesn't
- // always work for nested conditional expressions. The test values
- // that we generate on the fly to probe expressions can trigger
- // code flow decisions in multiple nodes of the decision tree, and the
- // only way to compute the correct error code in that situation is by
- // calling EvaluateErrorCode().
- *err = "Exit code from BPF program doesn't match";
- return false;
- }
- } else if (code.error_type() == ErrorCode::ET_COND) {
- if (code.argno() < 0 || code.argno() >= 6) {
- *err = "Invalid argument number in error code";
- return false;
- }
-
- // TODO(mdempsky): The test values generated here try to provide good
- // coverage for generated BPF instructions while avoiding combinatorial
- // explosion on large policies. Ideally we would instead take a fuzzing-like
- // approach and generate a bounded number of test cases regardless of policy
- // size.
-
- // Verify that we can check a value for simple equality.
- data->args[code.argno()] = code.value();
- if (!VerifyErrorCode(
- compiler, program, data, root_code, *code.passed(), err)) {
- return false;
- }
-
- // If mask ignores any bits, verify that setting those bits is still
- // detected as equality.
- uint64_t ignored_bits = ~code.mask();
- if (code.width() == ErrorCode::TP_32BIT) {
- ignored_bits = static_cast<uint32_t>(ignored_bits);
- }
- if ((ignored_bits & kLower32Bits) != 0) {
- data->args[code.argno()] = code.value() | (ignored_bits & kLower32Bits);
- if (!VerifyErrorCode(
- compiler, program, data, root_code, *code.passed(), err)) {
- return false;
- }
- }
- if ((ignored_bits & kUpper32Bits) != 0) {
- data->args[code.argno()] = code.value() | (ignored_bits & kUpper32Bits);
- if (!VerifyErrorCode(
- compiler, program, data, root_code, *code.passed(), err)) {
- return false;
- }
- }
-
- // Verify that changing bits included in the mask is detected as inequality.
- if ((code.mask() & kLower32Bits) != 0) {
- data->args[code.argno()] = code.value() ^ (code.mask() & kLower32Bits);
- if (!VerifyErrorCode(
- compiler, program, data, root_code, *code.failed(), err)) {
- return false;
- }
- }
- if ((code.mask() & kUpper32Bits) != 0) {
- data->args[code.argno()] = code.value() ^ (code.mask() & kUpper32Bits);
- if (!VerifyErrorCode(
- compiler, program, data, root_code, *code.failed(), err)) {
- return false;
- }
- }
-
- if (code.width() == ErrorCode::TP_32BIT) {
- // For 32-bit system call arguments, we emit additional instructions to
- // validate the upper 32-bits. Here we test that validation.
-
- // Arbitrary 64-bit values should be rejected.
- data->args[code.argno()] = 1ULL << 32;
- if (!VerifyErrorCode(compiler,
- program,
- data,
- root_code,
- compiler->Unexpected64bitArgument(),
- err)) {
- return false;
- }
-
- // Upper 32-bits set without the MSB of the lower 32-bits set should be
- // rejected too.
- data->args[code.argno()] = kUpper32Bits;
- if (!VerifyErrorCode(compiler,
- program,
- data,
- root_code,
- compiler->Unexpected64bitArgument(),
- err)) {
- return false;
- }
- }
- } else {
- *err = "Attempting to return invalid error code from BPF program";
- return false;
- }
- 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 ||
- 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<const char*>(&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
-
-bool Verifier::VerifyBPF(bpf_dsl::PolicyCompiler* compiler,
- const std::vector<struct sock_filter>& program,
- const bpf_dsl::Policy& policy,
- const char** err) {
- *err = NULL;
- for (uint32_t sysnum : SyscallSet::All()) {
- // 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
- // of the "evaluators".
- // On Intel systems, this can fail in a surprising way, as a cleared bit 30
- // 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)};
-#if defined(__i386__) || defined(__x86_64__)
-#if defined(__x86_64__) && defined(__ILP32__)
- if (!(sysnum & 0x40000000u)) {
- continue;
- }
-#else
- if (sysnum & 0x40000000u) {
- continue;
- }
-#endif
-#endif
- ErrorCode code = SyscallSet::IsValid(sysnum)
- ? policy.EvaluateSyscall(sysnum)->Compile(compiler)
- : policy.InvalidSyscall()->Compile(compiler);
- if (!VerifyErrorCode(compiler, program, &data, code, code, err)) {
- return false;
- }
- }
- return true;
-}
-
-uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& 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_TRAP:
- case SECCOMP_RET_ERRNO:
- case SECCOMP_RET_TRACE:
- case SECCOMP_RET_ALLOW:
- break;
- case SECCOMP_RET_KILL: // 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;
- }
- case BPF_ALU:
- Alu(&state, insn, err);
- break;
- default:
- *err = "Unexpected instruction in BPF program";
- break;
- }
- }
- return 0;
-}
-
-} // namespace sandbox
« no previous file with comments | « sandbox/linux/seccomp-bpf/verifier.h ('k') | sdch/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698