// Copyright 2014 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/bpf_dsl.h" #include #include #include #include "base/logging.h" #include "base/macros.h" #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" #include "sandbox/linux/bpf_dsl/errorcode.h" #include "sandbox/linux/bpf_dsl/policy_compiler.h" #include "sandbox/linux/system_headers/linux_seccomp.h" namespace sandbox { namespace bpf_dsl { namespace { class ReturnResultExprImpl : public internal::ResultExprImpl { public: explicit ReturnResultExprImpl(uint32_t ret) : ret_(ret) {} ~ReturnResultExprImpl() override {} CodeGen::Node Compile(PolicyCompiler* pc) const override { return pc->Return(ret_); } bool IsAllow() const override { return IsAction(SECCOMP_RET_ALLOW); } bool IsDeny() const override { return IsAction(SECCOMP_RET_ERRNO) || IsAction(SECCOMP_RET_KILL); } private: bool IsAction(uint32_t action) const { return (ret_ & SECCOMP_RET_ACTION) == action; } uint32_t ret_; DISALLOW_COPY_AND_ASSIGN(ReturnResultExprImpl); }; class TrapResultExprImpl : public internal::ResultExprImpl { public: TrapResultExprImpl(TrapRegistry::TrapFnc func, const void* arg, bool safe) : func_(func), arg_(arg), safe_(safe) { DCHECK(func_); } ~TrapResultExprImpl() override {} CodeGen::Node Compile(PolicyCompiler* pc) const override { return pc->Trap(func_, arg_, safe_); } bool HasUnsafeTraps() const override { return safe_ == false; } bool IsDeny() const override { return true; } private: TrapRegistry::TrapFnc func_; const void* arg_; bool safe_; DISALLOW_COPY_AND_ASSIGN(TrapResultExprImpl); }; class IfThenResultExprImpl : public internal::ResultExprImpl { public: IfThenResultExprImpl(BoolExpr cond, ResultExpr then_result, ResultExpr else_result) : cond_(std::move(cond)), then_result_(std::move(then_result)), else_result_(std::move(else_result)) {} ~IfThenResultExprImpl() override {} CodeGen::Node Compile(PolicyCompiler* pc) const override { // We compile the "then" and "else" expressions in separate statements so // they have a defined sequencing. See https://crbug.com/529480. CodeGen::Node then_node = then_result_->Compile(pc); CodeGen::Node else_node = else_result_->Compile(pc); return cond_->Compile(pc, then_node, else_node); } bool HasUnsafeTraps() const override { return then_result_->HasUnsafeTraps() || else_result_->HasUnsafeTraps(); } private: BoolExpr cond_; ResultExpr then_result_; ResultExpr else_result_; DISALLOW_COPY_AND_ASSIGN(IfThenResultExprImpl); }; class ConstBoolExprImpl : public internal::BoolExprImpl { public: ConstBoolExprImpl(bool value) : value_(value) {} ~ConstBoolExprImpl() override {} CodeGen::Node Compile(PolicyCompiler* pc, CodeGen::Node then_node, CodeGen::Node else_node) const override { return value_ ? then_node : else_node; } private: bool value_; DISALLOW_COPY_AND_ASSIGN(ConstBoolExprImpl); }; class MaskedEqualBoolExprImpl : public internal::BoolExprImpl { public: MaskedEqualBoolExprImpl(int argno, size_t width, uint64_t mask, uint64_t value) : argno_(argno), width_(width), mask_(mask), value_(value) {} ~MaskedEqualBoolExprImpl() override {} CodeGen::Node Compile(PolicyCompiler* pc, CodeGen::Node then_node, CodeGen::Node else_node) const override { return pc->MaskedEqual(argno_, width_, mask_, value_, then_node, else_node); } private: int argno_; size_t width_; uint64_t mask_; uint64_t value_; DISALLOW_COPY_AND_ASSIGN(MaskedEqualBoolExprImpl); }; class NegateBoolExprImpl : public internal::BoolExprImpl { public: explicit NegateBoolExprImpl(BoolExpr cond) : cond_(std::move(cond)) {} ~NegateBoolExprImpl() override {} CodeGen::Node Compile(PolicyCompiler* pc, CodeGen::Node then_node, CodeGen::Node else_node) const override { return cond_->Compile(pc, else_node, then_node); } private: BoolExpr cond_; DISALLOW_COPY_AND_ASSIGN(NegateBoolExprImpl); }; class AndBoolExprImpl : public internal::BoolExprImpl { public: AndBoolExprImpl(BoolExpr lhs, BoolExpr rhs) : lhs_(std::move(lhs)), rhs_(std::move(rhs)) {} ~AndBoolExprImpl() override {} CodeGen::Node Compile(PolicyCompiler* pc, CodeGen::Node then_node, CodeGen::Node else_node) const override { return lhs_->Compile(pc, rhs_->Compile(pc, then_node, else_node), else_node); } private: BoolExpr lhs_; BoolExpr rhs_; DISALLOW_COPY_AND_ASSIGN(AndBoolExprImpl); }; class OrBoolExprImpl : public internal::BoolExprImpl { public: OrBoolExprImpl(BoolExpr lhs, BoolExpr rhs) : lhs_(std::move(lhs)), rhs_(std::move(rhs)) {} ~OrBoolExprImpl() override {} CodeGen::Node Compile(PolicyCompiler* pc, CodeGen::Node then_node, CodeGen::Node else_node) const override { return lhs_->Compile(pc, then_node, rhs_->Compile(pc, then_node, else_node)); } private: BoolExpr lhs_; BoolExpr rhs_; DISALLOW_COPY_AND_ASSIGN(OrBoolExprImpl); }; } // namespace namespace internal { bool ResultExprImpl::HasUnsafeTraps() const { return false; } bool ResultExprImpl::IsAllow() const { return false; } bool ResultExprImpl::IsDeny() const { return false; } uint64_t DefaultMask(size_t size) { switch (size) { case 4: return std::numeric_limits::max(); case 8: return std::numeric_limits::max(); default: CHECK(false) << "Unimplemented DefaultMask case"; return 0; } } BoolExpr ArgEq(int num, size_t size, uint64_t mask, uint64_t val) { // If this is changed, update Arg::EqualTo's static_cast rules // accordingly. CHECK(size == 4 || size == 8); return std::make_shared(num, size, mask, val); } } // namespace internal ResultExpr Allow() { return std::make_shared(SECCOMP_RET_ALLOW); } ResultExpr Error(int err) { CHECK(err >= ErrorCode::ERR_MIN_ERRNO && err <= ErrorCode::ERR_MAX_ERRNO); return std::make_shared(SECCOMP_RET_ERRNO + err); } ResultExpr Kill() { return std::make_shared(SECCOMP_RET_KILL); } ResultExpr Trace(uint16_t aux) { return std::make_shared(SECCOMP_RET_TRACE + aux); } ResultExpr Trap(TrapRegistry::TrapFnc trap_func, const void* aux) { return std::make_shared(trap_func, aux, true /* safe */); } ResultExpr UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux) { return std::make_shared(trap_func, aux, false /* unsafe */); } BoolExpr BoolConst(bool value) { return std::make_shared(value); } BoolExpr Not(BoolExpr cond) { return std::make_shared(std::move(cond)); } BoolExpr AllOf() { return BoolConst(true); } BoolExpr AllOf(BoolExpr lhs, BoolExpr rhs) { return std::make_shared(std::move(lhs), std::move(rhs)); } BoolExpr AnyOf() { return BoolConst(false); } BoolExpr AnyOf(BoolExpr lhs, BoolExpr rhs) { return std::make_shared(std::move(lhs), std::move(rhs)); } Elser If(BoolExpr cond, ResultExpr then_result) { return Elser(nullptr).ElseIf(std::move(cond), std::move(then_result)); } Elser::Elser(cons::List clause_list) : clause_list_(clause_list) { } Elser::Elser(const Elser& elser) : clause_list_(elser.clause_list_) { } Elser::~Elser() { } Elser Elser::ElseIf(BoolExpr cond, ResultExpr then_result) const { return Elser(Cons(std::make_pair(std::move(cond), std::move(then_result)), clause_list_)); } ResultExpr Elser::Else(ResultExpr else_result) const { // We finally have the default result expression for this // if/then/else sequence. Also, we've already accumulated all // if/then pairs into a list of reverse order (i.e., lower priority // conditions are listed before higher priority ones). E.g., an // expression like // // If(b1, e1).ElseIf(b2, e2).ElseIf(b3, e3).Else(e4) // // will have built up a list like // // [(b3, e3), (b2, e2), (b1, e1)]. // // Now that we have e4, we can walk the list and create a ResultExpr // tree like: // // expr = e4 // expr = (b3 ? e3 : expr) = (b3 ? e3 : e4) // expr = (b2 ? e2 : expr) = (b2 ? e2 : (b3 ? e3 : e4)) // expr = (b1 ? e1 : expr) = (b1 ? e1 : (b2 ? e2 : (b3 ? e3 : e4))) // // and end up with an appropriately chained tree. ResultExpr expr = std::move(else_result); for (const Clause& clause : clause_list_) { expr = std::make_shared(clause.first, clause.second, std::move(expr)); } return expr; } } // namespace bpf_dsl } // namespace sandbox namespace std { template class shared_ptr; template class shared_ptr; } // namespace std