| Index: sandbox/linux/bpf_dsl/policy_compiler.cc
|
| diff --git a/sandbox/linux/bpf_dsl/policy_compiler.cc b/sandbox/linux/bpf_dsl/policy_compiler.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0eb85ca6736bb3662dbf520b13d7723e7c2cc3d8
|
| --- /dev/null
|
| +++ b/sandbox/linux/bpf_dsl/policy_compiler.cc
|
| @@ -0,0 +1,523 @@
|
| +// 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/policy_compiler.h"
|
| +
|
| +#include <errno.h>
|
| +#include <linux/filter.h>
|
| +#include <sys/syscall.h>
|
| +
|
| +#include <limits>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/macros.h"
|
| +#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
|
| +#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
|
| +#include "sandbox/linux/seccomp-bpf/codegen.h"
|
| +#include "sandbox/linux/seccomp-bpf/die.h"
|
| +#include "sandbox/linux/seccomp-bpf/errorcode.h"
|
| +#include "sandbox/linux/seccomp-bpf/instruction.h"
|
| +#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
|
| +#include "sandbox/linux/seccomp-bpf/syscall.h"
|
| +#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
|
| +
|
| +namespace sandbox {
|
| +namespace bpf_dsl {
|
| +
|
| +namespace {
|
| +
|
| +#if defined(__i386__) || defined(__x86_64__)
|
| +const bool kIsIntel = true;
|
| +#else
|
| +const bool kIsIntel = false;
|
| +#endif
|
| +#if defined(__x86_64__) && defined(__ILP32__)
|
| +const bool kIsX32 = true;
|
| +#else
|
| +const bool kIsX32 = false;
|
| +#endif
|
| +
|
| +const int kSyscallsRequiredForUnsafeTraps[] = {
|
| + __NR_rt_sigprocmask,
|
| + __NR_rt_sigreturn,
|
| +#if defined(__NR_sigprocmask)
|
| + __NR_sigprocmask,
|
| +#endif
|
| +#if defined(__NR_sigreturn)
|
| + __NR_sigreturn,
|
| +#endif
|
| +};
|
| +
|
| +bool HasExactlyOneBit(uint64_t x) {
|
| + // Common trick; e.g., see http://stackoverflow.com/a/108329.
|
| + return x != 0 && (x & (x - 1)) == 0;
|
| +}
|
| +
|
| +bool IsDenied(const ErrorCode& code) {
|
| + return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP ||
|
| + (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) &&
|
| + code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO));
|
| +}
|
| +
|
| +// A Trap() handler that returns an "errno" value. The value is encoded
|
| +// in the "aux" parameter.
|
| +intptr_t ReturnErrno(const struct arch_seccomp_data&, void* aux) {
|
| + // TrapFnc functions report error by following the native kernel convention
|
| + // of returning an exit code in the range of -1..-4096. They do not try to
|
| + // set errno themselves. The glibc wrapper that triggered the SIGSYS will
|
| + // ultimately do so for us.
|
| + int err = reinterpret_cast<intptr_t>(aux) & SECCOMP_RET_DATA;
|
| + return -err;
|
| +}
|
| +
|
| +intptr_t BPFFailure(const struct arch_seccomp_data&, void* aux) {
|
| + SANDBOX_DIE(static_cast<char*>(aux));
|
| +}
|
| +
|
| +bool HasUnsafeTraps(const SandboxBPFDSLPolicy* policy) {
|
| + for (uint32_t sysnum : SyscallSet::All()) {
|
| + if (SyscallSet::IsValid(sysnum) &&
|
| + policy->EvaluateSyscall(sysnum)->HasUnsafeTraps()) {
|
| + return true;
|
| + }
|
| + }
|
| + return policy->InvalidSyscall()->HasUnsafeTraps();
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +struct PolicyCompiler::Range {
|
| + Range(uint32_t f, const ErrorCode& e) : from(f), err(e) {}
|
| + uint32_t from;
|
| + ErrorCode err;
|
| +};
|
| +
|
| +PolicyCompiler::PolicyCompiler(const SandboxBPFDSLPolicy* policy,
|
| + TrapRegistry* registry)
|
| + : policy_(policy),
|
| + registry_(registry),
|
| + conds_(),
|
| + gen_(),
|
| + has_unsafe_traps_(HasUnsafeTraps(policy_)) {
|
| +}
|
| +
|
| +PolicyCompiler::~PolicyCompiler() {
|
| +}
|
| +
|
| +scoped_ptr<CodeGen::Program> PolicyCompiler::Compile() {
|
| + if (!IsDenied(policy_->InvalidSyscall()->Compile(this))) {
|
| + SANDBOX_DIE("Policies should deny invalid system calls.");
|
| + }
|
| +
|
| + // If our BPF program has unsafe traps, enable support for them.
|
| + if (has_unsafe_traps_) {
|
| + // As support for unsafe jumps essentially defeats all the security
|
| + // measures that the sandbox provides, we print a big warning message --
|
| + // and of course, we make sure to only ever enable this feature if it
|
| + // is actually requested by the sandbox policy.
|
| + if (Syscall::Call(-1) == -1 && errno == ENOSYS) {
|
| + SANDBOX_DIE(
|
| + "Support for UnsafeTrap() has not yet been ported to this "
|
| + "architecture");
|
| + }
|
| +
|
| + for (int sysnum : kSyscallsRequiredForUnsafeTraps) {
|
| + if (!policy_->EvaluateSyscall(sysnum)->Compile(this)
|
| + .Equals(ErrorCode(ErrorCode::ERR_ALLOWED))) {
|
| + SANDBOX_DIE(
|
| + "Policies that use UnsafeTrap() must unconditionally allow all "
|
| + "required system calls");
|
| + }
|
| + }
|
| +
|
| + if (!registry_->EnableUnsafeTraps()) {
|
| + // We should never be able to get here, as UnsafeTrap() should never
|
| + // actually return a valid ErrorCode object unless the user set the
|
| + // CHROME_SANDBOX_DEBUGGING environment variable; and therefore,
|
| + // "has_unsafe_traps" would always be false. But better double-check
|
| + // than enabling dangerous code.
|
| + SANDBOX_DIE("We'd rather die than enable unsafe traps");
|
| + }
|
| + }
|
| +
|
| + // Assemble the BPF filter program.
|
| + scoped_ptr<CodeGen::Program> program(new CodeGen::Program());
|
| + gen_.Compile(AssemblePolicy(), program.get());
|
| + return program.Pass();
|
| +}
|
| +
|
| +Instruction* PolicyCompiler::AssemblePolicy() {
|
| + // A compiled policy consists of three logical parts:
|
| + // 1. Check that the "arch" field matches the expected architecture.
|
| + // 2. If the policy involves unsafe traps, check if the syscall was
|
| + // invoked by Syscall::Call, and then allow it unconditionally.
|
| + // 3. Check the system call number and jump to the appropriate compiled
|
| + // system call policy number.
|
| + return CheckArch(MaybeAddEscapeHatch(DispatchSyscall()));
|
| +}
|
| +
|
| +Instruction* PolicyCompiler::CheckArch(Instruction* passed) {
|
| + // If the architecture doesn't match SECCOMP_ARCH, disallow the
|
| + // system call.
|
| + return gen_.MakeInstruction(
|
| + BPF_LD + BPF_W + BPF_ABS,
|
| + SECCOMP_ARCH_IDX,
|
| + gen_.MakeInstruction(
|
| + BPF_JMP + BPF_JEQ + BPF_K,
|
| + SECCOMP_ARCH,
|
| + passed,
|
| + RetExpression(Kill("Invalid audit architecture in BPF filter"))));
|
| +}
|
| +
|
| +Instruction* PolicyCompiler::MaybeAddEscapeHatch(Instruction* rest) {
|
| + // If no unsafe traps, then simply return |rest|.
|
| + if (!has_unsafe_traps_) {
|
| + return rest;
|
| + }
|
| +
|
| + // Allow system calls, if they originate from our magic return address
|
| + // (which we can query by calling Syscall::Call(-1)).
|
| + uint64_t syscall_entry_point =
|
| + static_cast<uint64_t>(static_cast<uintptr_t>(Syscall::Call(-1)));
|
| + uint32_t low = static_cast<uint32_t>(syscall_entry_point);
|
| + uint32_t hi = static_cast<uint32_t>(syscall_entry_point >> 32);
|
| +
|
| + // BPF cannot do native 64-bit comparisons, so we have to compare
|
| + // both 32-bit halves of the instruction pointer. If they match what
|
| + // we expect, we return ERR_ALLOWED. If either or both don't match,
|
| + // we continue evalutating the rest of the sandbox policy.
|
| + //
|
| + // For simplicity, we check the full 64-bit instruction pointer even
|
| + // on 32-bit architectures.
|
| + return gen_.MakeInstruction(
|
| + BPF_LD + BPF_W + BPF_ABS,
|
| + SECCOMP_IP_LSB_IDX,
|
| + gen_.MakeInstruction(
|
| + BPF_JMP + BPF_JEQ + BPF_K,
|
| + low,
|
| + gen_.MakeInstruction(
|
| + BPF_LD + BPF_W + BPF_ABS,
|
| + SECCOMP_IP_MSB_IDX,
|
| + gen_.MakeInstruction(
|
| + BPF_JMP + BPF_JEQ + BPF_K,
|
| + hi,
|
| + RetExpression(ErrorCode(ErrorCode::ERR_ALLOWED)),
|
| + rest)),
|
| + rest));
|
| +}
|
| +
|
| +Instruction* PolicyCompiler::DispatchSyscall() {
|
| + // Evaluate all possible system calls and group their ErrorCodes into
|
| + // ranges of identical codes.
|
| + Ranges ranges;
|
| + FindRanges(&ranges);
|
| +
|
| + // Compile the system call ranges to an optimized BPF jumptable
|
| + Instruction* jumptable = AssembleJumpTable(ranges.begin(), ranges.end());
|
| +
|
| + // Grab the system call number, so that we can check it and then
|
| + // execute the jump table.
|
| + return gen_.MakeInstruction(
|
| + BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX, CheckSyscallNumber(jumptable));
|
| +}
|
| +
|
| +Instruction* PolicyCompiler::CheckSyscallNumber(Instruction* passed) {
|
| + if (kIsIntel) {
|
| + // On Intel architectures, verify that system call numbers are in the
|
| + // expected number range.
|
| + Instruction* invalidX32 =
|
| + RetExpression(Kill("Illegal mixing of system call ABIs"));
|
| + if (kIsX32) {
|
| + // The newer x32 API always sets bit 30.
|
| + return gen_.MakeInstruction(
|
| + BPF_JMP + BPF_JSET + BPF_K, 0x40000000, passed, invalidX32);
|
| + } else {
|
| + // The older i386 and x86-64 APIs clear bit 30 on all system calls.
|
| + return gen_.MakeInstruction(
|
| + BPF_JMP + BPF_JSET + BPF_K, 0x40000000, invalidX32, passed);
|
| + }
|
| + }
|
| +
|
| + // TODO(mdempsky): Similar validation for other architectures?
|
| + return passed;
|
| +}
|
| +
|
| +void PolicyCompiler::FindRanges(Ranges* ranges) {
|
| + // Please note that "struct seccomp_data" defines system calls as a signed
|
| + // int32_t, but BPF instructions always operate on unsigned quantities. We
|
| + // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL,
|
| + // and then verifying that the rest of the number range (both positive and
|
| + // negative) all return the same ErrorCode.
|
| + const ErrorCode invalid_err = policy_->InvalidSyscall()->Compile(this);
|
| + uint32_t old_sysnum = 0;
|
| + ErrorCode old_err = SyscallSet::IsValid(old_sysnum)
|
| + ? policy_->EvaluateSyscall(old_sysnum)->Compile(this)
|
| + : invalid_err;
|
| +
|
| + for (uint32_t sysnum : SyscallSet::All()) {
|
| + ErrorCode err =
|
| + SyscallSet::IsValid(sysnum)
|
| + ? policy_->EvaluateSyscall(static_cast<int>(sysnum))->Compile(this)
|
| + : invalid_err;
|
| + if (!err.Equals(old_err)) {
|
| + ranges->push_back(Range(old_sysnum, old_err));
|
| + old_sysnum = sysnum;
|
| + old_err = err;
|
| + }
|
| + }
|
| + ranges->push_back(Range(old_sysnum, old_err));
|
| +}
|
| +
|
| +Instruction* PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start,
|
| + Ranges::const_iterator stop) {
|
| + // We convert the list of system call ranges into jump table that performs
|
| + // a binary search over the ranges.
|
| + // As a sanity check, we need to have at least one distinct ranges for us
|
| + // to be able to build a jump table.
|
| + if (stop - start <= 0) {
|
| + SANDBOX_DIE("Invalid set of system call ranges");
|
| + } else if (stop - start == 1) {
|
| + // If we have narrowed things down to a single range object, we can
|
| + // return from the BPF filter program.
|
| + return RetExpression(start->err);
|
| + }
|
| +
|
| + // Pick the range object that is located at the mid point of our list.
|
| + // We compare our system call number against the lowest valid system call
|
| + // number in this range object. If our number is lower, it is outside of
|
| + // this range object. If it is greater or equal, it might be inside.
|
| + Ranges::const_iterator mid = start + (stop - start) / 2;
|
| +
|
| + // Sub-divide the list of ranges and continue recursively.
|
| + Instruction* jf = AssembleJumpTable(start, mid);
|
| + Instruction* jt = AssembleJumpTable(mid, stop);
|
| + return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf);
|
| +}
|
| +
|
| +Instruction* PolicyCompiler::RetExpression(const ErrorCode& err) {
|
| + switch (err.error_type()) {
|
| + case ErrorCode::ET_COND:
|
| + return CondExpression(err);
|
| + case ErrorCode::ET_SIMPLE:
|
| + case ErrorCode::ET_TRAP:
|
| + return gen_.MakeInstruction(BPF_RET + BPF_K, err.err());
|
| + default:
|
| + SANDBOX_DIE("ErrorCode is not suitable for returning from a BPF program");
|
| + }
|
| +}
|
| +
|
| +Instruction* PolicyCompiler::CondExpression(const ErrorCode& cond) {
|
| + // Sanity check that |cond| makes sense.
|
| + if (cond.argno_ < 0 || cond.argno_ >= 6) {
|
| + SANDBOX_DIE("sandbox_bpf: invalid argument number");
|
| + }
|
| + if (cond.width_ != ErrorCode::TP_32BIT &&
|
| + cond.width_ != ErrorCode::TP_64BIT) {
|
| + SANDBOX_DIE("sandbox_bpf: invalid argument width");
|
| + }
|
| + if (cond.mask_ == 0) {
|
| + SANDBOX_DIE("sandbox_bpf: zero mask is invalid");
|
| + }
|
| + if ((cond.value_ & cond.mask_) != cond.value_) {
|
| + SANDBOX_DIE("sandbox_bpf: value contains masked out bits");
|
| + }
|
| + if (cond.width_ == ErrorCode::TP_32BIT &&
|
| + ((cond.mask_ >> 32) != 0 || (cond.value_ >> 32) != 0)) {
|
| + SANDBOX_DIE("sandbox_bpf: test exceeds argument size");
|
| + }
|
| + // TODO(mdempsky): Reject TP_64BIT on 32-bit platforms. For now we allow it
|
| + // because some SandboxBPF unit tests exercise it.
|
| +
|
| + Instruction* passed = RetExpression(*cond.passed_);
|
| + Instruction* failed = RetExpression(*cond.failed_);
|
| +
|
| + // We want to emit code to check "(arg & mask) == value" where arg, mask, and
|
| + // value are 64-bit values, but the BPF machine is only 32-bit. We implement
|
| + // this by independently testing the upper and lower 32-bits and continuing to
|
| + // |passed| if both evaluate true, or to |failed| if either evaluate false.
|
| + return CondExpressionHalf(cond,
|
| + UpperHalf,
|
| + CondExpressionHalf(cond, LowerHalf, passed, failed),
|
| + failed);
|
| +}
|
| +
|
| +Instruction* PolicyCompiler::CondExpressionHalf(const ErrorCode& cond,
|
| + ArgHalf half,
|
| + Instruction* passed,
|
| + Instruction* failed) {
|
| + if (cond.width_ == ErrorCode::TP_32BIT && half == UpperHalf) {
|
| + // Special logic for sanity checking the upper 32-bits of 32-bit system
|
| + // call arguments.
|
| +
|
| + // TODO(mdempsky): Compile Unexpected64bitArgument() just per program.
|
| + Instruction* invalid_64bit = RetExpression(Unexpected64bitArgument());
|
| +
|
| + const uint32_t upper = SECCOMP_ARG_MSB_IDX(cond.argno_);
|
| + const uint32_t lower = SECCOMP_ARG_LSB_IDX(cond.argno_);
|
| +
|
| + if (sizeof(void*) == 4) {
|
| + // On 32-bit platforms, the upper 32-bits should always be 0:
|
| + // LDW [upper]
|
| + // JEQ 0, passed, invalid
|
| + return gen_.MakeInstruction(
|
| + BPF_LD + BPF_W + BPF_ABS,
|
| + upper,
|
| + gen_.MakeInstruction(
|
| + BPF_JMP + BPF_JEQ + BPF_K, 0, passed, invalid_64bit));
|
| + }
|
| +
|
| + // On 64-bit platforms, the upper 32-bits may be 0 or ~0; but we only allow
|
| + // ~0 if the sign bit of the lower 32-bits is set too:
|
| + // LDW [upper]
|
| + // JEQ 0, passed, (next)
|
| + // JEQ ~0, (next), invalid
|
| + // LDW [lower]
|
| + // JSET (1<<31), passed, invalid
|
| + //
|
| + // TODO(mdempsky): The JSET instruction could perhaps jump to passed->next
|
| + // instead, as the first instruction of passed should be "LDW [lower]".
|
| + return gen_.MakeInstruction(
|
| + BPF_LD + BPF_W + BPF_ABS,
|
| + upper,
|
| + gen_.MakeInstruction(
|
| + BPF_JMP + BPF_JEQ + BPF_K,
|
| + 0,
|
| + passed,
|
| + gen_.MakeInstruction(
|
| + BPF_JMP + BPF_JEQ + BPF_K,
|
| + std::numeric_limits<uint32_t>::max(),
|
| + gen_.MakeInstruction(
|
| + BPF_LD + BPF_W + BPF_ABS,
|
| + lower,
|
| + gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
|
| + 1U << 31,
|
| + passed,
|
| + invalid_64bit)),
|
| + invalid_64bit)));
|
| + }
|
| +
|
| + const uint32_t idx = (half == UpperHalf) ? SECCOMP_ARG_MSB_IDX(cond.argno_)
|
| + : SECCOMP_ARG_LSB_IDX(cond.argno_);
|
| + const uint32_t mask = (half == UpperHalf) ? cond.mask_ >> 32 : cond.mask_;
|
| + const uint32_t value = (half == UpperHalf) ? cond.value_ >> 32 : cond.value_;
|
| +
|
| + // Emit a suitable instruction sequence for (arg & mask) == value.
|
| +
|
| + // For (arg & 0) == 0, just return passed.
|
| + if (mask == 0) {
|
| + CHECK_EQ(0U, value);
|
| + return passed;
|
| + }
|
| +
|
| + // For (arg & ~0) == value, emit:
|
| + // LDW [idx]
|
| + // JEQ value, passed, failed
|
| + if (mask == std::numeric_limits<uint32_t>::max()) {
|
| + return gen_.MakeInstruction(
|
| + BPF_LD + BPF_W + BPF_ABS,
|
| + idx,
|
| + gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed));
|
| + }
|
| +
|
| + // For (arg & mask) == 0, emit:
|
| + // LDW [idx]
|
| + // JSET mask, failed, passed
|
| + // (Note: failed and passed are intentionally swapped.)
|
| + if (value == 0) {
|
| + return gen_.MakeInstruction(
|
| + BPF_LD + BPF_W + BPF_ABS,
|
| + idx,
|
| + gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, mask, failed, passed));
|
| + }
|
| +
|
| + // For (arg & x) == x where x is a single-bit value, emit:
|
| + // LDW [idx]
|
| + // JSET mask, passed, failed
|
| + if (mask == value && HasExactlyOneBit(mask)) {
|
| + return gen_.MakeInstruction(
|
| + BPF_LD + BPF_W + BPF_ABS,
|
| + idx,
|
| + gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, mask, passed, failed));
|
| + }
|
| +
|
| + // Generic fallback:
|
| + // LDW [idx]
|
| + // AND mask
|
| + // JEQ value, passed, failed
|
| + return gen_.MakeInstruction(
|
| + BPF_LD + BPF_W + BPF_ABS,
|
| + idx,
|
| + gen_.MakeInstruction(
|
| + BPF_ALU + BPF_AND + BPF_K,
|
| + mask,
|
| + gen_.MakeInstruction(
|
| + BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed)));
|
| +}
|
| +
|
| +ErrorCode PolicyCompiler::Unexpected64bitArgument() {
|
| + return Kill("Unexpected 64bit argument detected");
|
| +}
|
| +
|
| +ErrorCode PolicyCompiler::Error(int err) {
|
| + if (has_unsafe_traps_) {
|
| + // When inside an UnsafeTrap() callback, we want to allow all system calls.
|
| + // This means, we must conditionally disable the sandbox -- and that's not
|
| + // something that kernel-side BPF filters can do, as they cannot inspect
|
| + // any state other than the syscall arguments.
|
| + // But if we redirect all error handlers to user-space, then we can easily
|
| + // make this decision.
|
| + // The performance penalty for this extra round-trip to user-space is not
|
| + // actually that bad, as we only ever pay it for denied system calls; and a
|
| + // typical program has very few of these.
|
| + return Trap(ReturnErrno, reinterpret_cast<void*>(err));
|
| + }
|
| +
|
| + return ErrorCode(err);
|
| +}
|
| +
|
| +ErrorCode PolicyCompiler::MakeTrap(TrapRegistry::TrapFnc fnc,
|
| + const void* aux,
|
| + bool safe) {
|
| + uint16_t trap_id = registry_->Add(fnc, aux, safe);
|
| + return ErrorCode(trap_id, fnc, aux, safe);
|
| +}
|
| +
|
| +ErrorCode PolicyCompiler::Trap(TrapRegistry::TrapFnc fnc, const void* aux) {
|
| + return MakeTrap(fnc, aux, true /* Safe Trap */);
|
| +}
|
| +
|
| +ErrorCode PolicyCompiler::UnsafeTrap(TrapRegistry::TrapFnc fnc,
|
| + const void* aux) {
|
| + return MakeTrap(fnc, aux, false /* Unsafe Trap */);
|
| +}
|
| +
|
| +bool PolicyCompiler::IsRequiredForUnsafeTrap(int sysno) {
|
| + for (size_t i = 0; i < arraysize(kSyscallsRequiredForUnsafeTraps); ++i) {
|
| + if (sysno == kSyscallsRequiredForUnsafeTraps[i]) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +ErrorCode PolicyCompiler::CondMaskedEqual(int argno,
|
| + ErrorCode::ArgType width,
|
| + uint64_t mask,
|
| + uint64_t value,
|
| + const ErrorCode& passed,
|
| + const ErrorCode& failed) {
|
| + return ErrorCode(argno,
|
| + width,
|
| + mask,
|
| + value,
|
| + &*conds_.insert(passed).first,
|
| + &*conds_.insert(failed).first);
|
| +}
|
| +
|
| +ErrorCode PolicyCompiler::Kill(const char* msg) {
|
| + return Trap(BPFFailure, const_cast<char*>(msg));
|
| +}
|
| +
|
| +} // namespace bpf_dsl
|
| +} // namespace sandbox
|
|
|