diff options
Diffstat (limited to 'sandbox/linux/bpf_dsl')
25 files changed, 0 insertions, 4039 deletions
diff --git a/sandbox/linux/bpf_dsl/DEPS b/sandbox/linux/bpf_dsl/DEPS deleted file mode 100644 index 70d9b18aa1..0000000000 --- a/sandbox/linux/bpf_dsl/DEPS +++ /dev/null @@ -1,3 +0,0 @@ -include_rules = [ - "+sandbox/linux/system_headers", -] diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.cc b/sandbox/linux/bpf_dsl/bpf_dsl.cc deleted file mode 100644 index fed6368db6..0000000000 --- a/sandbox/linux/bpf_dsl/bpf_dsl.cc +++ /dev/null @@ -1,343 +0,0 @@ -// 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 <stddef.h> -#include <stdint.h> - -#include <limits> - -#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<uint32_t>::max(); - case 8: - return std::numeric_limits<uint64_t>::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<T>::EqualTo's static_cast rules - // accordingly. - CHECK(size == 4 || size == 8); - - return std::make_shared<MaskedEqualBoolExprImpl>(num, size, mask, val); -} - -} // namespace internal - -ResultExpr Allow() { - return std::make_shared<ReturnResultExprImpl>(SECCOMP_RET_ALLOW); -} - -ResultExpr Error(int err) { - CHECK(err >= ErrorCode::ERR_MIN_ERRNO && err <= ErrorCode::ERR_MAX_ERRNO); - return std::make_shared<ReturnResultExprImpl>(SECCOMP_RET_ERRNO + err); -} - -ResultExpr Kill() { - return std::make_shared<ReturnResultExprImpl>(SECCOMP_RET_KILL); -} - -ResultExpr Trace(uint16_t aux) { - return std::make_shared<ReturnResultExprImpl>(SECCOMP_RET_TRACE + aux); -} - -ResultExpr Trap(TrapRegistry::TrapFnc trap_func, const void* aux) { - return std::make_shared<TrapResultExprImpl>(trap_func, aux, true /* safe */); -} - -ResultExpr UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux) { - return std::make_shared<TrapResultExprImpl>(trap_func, aux, - false /* unsafe */); -} - -BoolExpr BoolConst(bool value) { - return std::make_shared<ConstBoolExprImpl>(value); -} - -BoolExpr Not(BoolExpr cond) { - return std::make_shared<NegateBoolExprImpl>(std::move(cond)); -} - -BoolExpr AllOf() { - return BoolConst(true); -} - -BoolExpr AllOf(BoolExpr lhs, BoolExpr rhs) { - return std::make_shared<AndBoolExprImpl>(std::move(lhs), std::move(rhs)); -} - -BoolExpr AnyOf() { - return BoolConst(false); -} - -BoolExpr AnyOf(BoolExpr lhs, BoolExpr rhs) { - return std::make_shared<OrBoolExprImpl>(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> 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<IfThenResultExprImpl>(clause.first, clause.second, - std::move(expr)); - } - return expr; -} - -} // namespace bpf_dsl -} // namespace sandbox - -namespace std { -template class shared_ptr<const sandbox::bpf_dsl::internal::BoolExprImpl>; -template class shared_ptr<const sandbox::bpf_dsl::internal::ResultExprImpl>; -} // namespace std diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.h b/sandbox/linux/bpf_dsl/bpf_dsl.h deleted file mode 100644 index 6f0dd4eb39..0000000000 --- a/sandbox/linux/bpf_dsl/bpf_dsl.h +++ /dev/null @@ -1,340 +0,0 @@ -// 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. - -#ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_ -#define SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <memory> -#include <utility> -#include <vector> - -#include "base/macros.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h" -#include "sandbox/linux/bpf_dsl/cons.h" -#include "sandbox/linux/bpf_dsl/trap_registry.h" -#include "sandbox/sandbox_export.h" - -// The sandbox::bpf_dsl namespace provides a domain-specific language -// to make writing BPF policies more expressive. In general, the -// object types all have value semantics (i.e., they can be copied -// around, returned from or passed to function calls, etc. without any -// surprising side effects), though not all support assignment. -// -// An idiomatic and demonstrative (albeit silly) example of this API -// would be: -// -// #include "sandbox/linux/bpf_dsl/bpf_dsl.h" -// -// using namespace sandbox::bpf_dsl; -// -// class SillyPolicy : public Policy { -// public: -// SillyPolicy() {} -// ~SillyPolicy() override {} -// ResultExpr EvaluateSyscall(int sysno) const override { -// if (sysno == __NR_fcntl) { -// Arg<int> fd(0), cmd(1); -// Arg<unsigned long> flags(2); -// const uint64_t kGoodFlags = O_ACCMODE | O_NONBLOCK; -// return If(AllOf(fd == 0, -// cmd == F_SETFL, -// (flags & ~kGoodFlags) == 0), -// Allow()) -// .ElseIf(AnyOf(cmd == F_DUPFD, cmd == F_DUPFD_CLOEXEC), -// Error(EMFILE)) -// .Else(Trap(SetFlagHandler, NULL)); -// } else { -// return Allow(); -// } -// } -// -// private: -// DISALLOW_COPY_AND_ASSIGN(SillyPolicy); -// }; -// -// More generally, the DSL currently supports the following grammar: -// -// result = Allow() | Error(errno) | Kill() | Trace(aux) -// | Trap(trap_func, aux) | UnsafeTrap(trap_func, aux) -// | If(bool, result)[.ElseIf(bool, result)].Else(result) -// | Switch(arg)[.Case(val, result)].Default(result) -// bool = BoolConst(boolean) | Not(bool) | AllOf(bool...) | AnyOf(bool...) -// | arg == val | arg != val -// arg = Arg<T>(num) | arg & mask -// -// The semantics of each function and operator are intended to be -// intuitive, but are described in more detail below. -// -// (Credit to Sean Parent's "Inheritance is the Base Class of Evil" -// talk at Going Native 2013 for promoting value semantics via shared -// pointers to immutable state.) - -namespace sandbox { -namespace bpf_dsl { - -template <typename T> -class Caser; - -class Elser; - -// ResultExpr is an opaque reference to an immutable result expression tree. -using ResultExpr = std::shared_ptr<const internal::ResultExprImpl>; - -// BoolExpr is an opaque reference to an immutable boolean expression tree. -using BoolExpr = std::shared_ptr<const internal::BoolExprImpl>; - -// Allow specifies a result that the system call should be allowed to -// execute normally. -SANDBOX_EXPORT ResultExpr Allow(); - -// Error specifies a result that the system call should fail with -// error number |err|. As a special case, Error(0) will result in the -// system call appearing to have succeeded, but without having any -// side effects. -SANDBOX_EXPORT ResultExpr Error(int err); - -// Kill specifies a result to kill the process (task) immediately. -SANDBOX_EXPORT ResultExpr Kill(); - -// Trace specifies a result to notify a tracing process via the -// PTRACE_EVENT_SECCOMP event and allow it to change or skip the system call. -// The value of |aux| will be available to the tracer via PTRACE_GETEVENTMSG. -SANDBOX_EXPORT ResultExpr Trace(uint16_t aux); - -// Trap specifies a result that the system call should be handled by -// trapping back into userspace and invoking |trap_func|, passing -// |aux| as the second parameter. -SANDBOX_EXPORT ResultExpr - Trap(TrapRegistry::TrapFnc trap_func, const void* aux); - -// UnsafeTrap is like Trap, except the policy is marked as "unsafe" -// and allowed to use SandboxSyscall to invoke any system call. -// -// NOTE: This feature, by definition, disables all security features of -// the sandbox. It should never be used in production, but it can be -// very useful to diagnose code that is incompatible with the sandbox. -// If even a single system call returns "UnsafeTrap", the security of -// entire sandbox should be considered compromised. -SANDBOX_EXPORT ResultExpr - UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux); - -// BoolConst converts a bool value into a BoolExpr. -SANDBOX_EXPORT BoolExpr BoolConst(bool value); - -// Not returns a BoolExpr representing the logical negation of |cond|. -SANDBOX_EXPORT BoolExpr Not(BoolExpr cond); - -// AllOf returns a BoolExpr representing the logical conjunction ("and") -// of zero or more BoolExprs. -SANDBOX_EXPORT BoolExpr AllOf(); -SANDBOX_EXPORT BoolExpr AllOf(BoolExpr lhs, BoolExpr rhs); -template <typename... Rest> -SANDBOX_EXPORT BoolExpr AllOf(BoolExpr first, Rest&&... rest); - -// AnyOf returns a BoolExpr representing the logical disjunction ("or") -// of zero or more BoolExprs. -SANDBOX_EXPORT BoolExpr AnyOf(); -SANDBOX_EXPORT BoolExpr AnyOf(BoolExpr lhs, BoolExpr rhs); -template <typename... Rest> -SANDBOX_EXPORT BoolExpr AnyOf(BoolExpr first, Rest&&... rest); - -template <typename T> -class SANDBOX_EXPORT Arg { - public: - // Initializes the Arg to represent the |num|th system call - // argument (indexed from 0), which is of type |T|. - explicit Arg(int num); - - Arg(const Arg& arg) : num_(arg.num_), mask_(arg.mask_) {} - - // Returns an Arg representing the current argument, but after - // bitwise-and'ing it with |rhs|. - friend Arg operator&(const Arg& lhs, uint64_t rhs) { - return Arg(lhs.num_, lhs.mask_ & rhs); - } - - // Returns a boolean expression comparing whether the system call argument - // (after applying any bitmasks, if appropriate) equals |rhs|. - friend BoolExpr operator==(const Arg& lhs, T rhs) { return lhs.EqualTo(rhs); } - - // Returns a boolean expression comparing whether the system call argument - // (after applying any bitmasks, if appropriate) does not equal |rhs|. - friend BoolExpr operator!=(const Arg& lhs, T rhs) { return Not(lhs == rhs); } - - private: - Arg(int num, uint64_t mask) : num_(num), mask_(mask) {} - - BoolExpr EqualTo(T val) const; - - int num_; - uint64_t mask_; - - DISALLOW_ASSIGN(Arg); -}; - -// If begins a conditional result expression predicated on the -// specified boolean expression. -SANDBOX_EXPORT Elser If(BoolExpr cond, ResultExpr then_result); - -class SANDBOX_EXPORT Elser { - public: - Elser(const Elser& elser); - ~Elser(); - - // ElseIf extends the conditional result expression with another - // "if then" clause, predicated on the specified boolean expression. - Elser ElseIf(BoolExpr cond, ResultExpr then_result) const; - - // Else terminates a conditional result expression using |else_result| as - // the default fallback result expression. - ResultExpr Else(ResultExpr else_result) const; - - private: - using Clause = std::pair<BoolExpr, ResultExpr>; - - explicit Elser(cons::List<Clause> clause_list); - - cons::List<Clause> clause_list_; - - friend Elser If(BoolExpr, ResultExpr); - template <typename T> - friend Caser<T> Switch(const Arg<T>&); - DISALLOW_ASSIGN(Elser); -}; - -// Switch begins a switch expression dispatched according to the -// specified argument value. -template <typename T> -SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg); - -template <typename T> -class SANDBOX_EXPORT Caser { - public: - Caser(const Caser<T>& caser) : arg_(caser.arg_), elser_(caser.elser_) {} - ~Caser() {} - - // Case adds a single-value "case" clause to the switch. - Caser<T> Case(T value, ResultExpr result) const; - - // Cases adds a multiple-value "case" clause to the switch. - // See also the SANDBOX_BPF_DSL_CASES macro below for a more idiomatic way - // of using this function. - template <typename... Values> - Caser<T> CasesImpl(ResultExpr result, const Values&... values) const; - - // Terminate the switch with a "default" clause. - ResultExpr Default(ResultExpr result) const; - - private: - Caser(const Arg<T>& arg, Elser elser) : arg_(arg), elser_(elser) {} - - Arg<T> arg_; - Elser elser_; - - template <typename U> - friend Caser<U> Switch(const Arg<U>&); - DISALLOW_ASSIGN(Caser); -}; - -// Recommended usage is to put -// #define CASES SANDBOX_BPF_DSL_CASES -// near the top of the .cc file (e.g., nearby any "using" statements), then -// use like: -// Switch(arg).CASES((3, 5, 7), result)...; -#define SANDBOX_BPF_DSL_CASES(values, result) \ - CasesImpl(result, SANDBOX_BPF_DSL_CASES_HELPER values) - -// Helper macro to strip parentheses. -#define SANDBOX_BPF_DSL_CASES_HELPER(...) __VA_ARGS__ - -// ===================================================================== -// Official API ends here. -// ===================================================================== - -namespace internal { - -// Make argument-dependent lookup work. This is necessary because although -// BoolExpr is defined in bpf_dsl, since it's merely a typedef for -// scoped_refptr<const internal::BoolExplImpl>, argument-dependent lookup only -// searches the "internal" nested namespace. -using bpf_dsl::Not; -using bpf_dsl::AllOf; -using bpf_dsl::AnyOf; - -// Returns a boolean expression that represents whether system call -// argument |num| of size |size| is equal to |val|, when masked -// according to |mask|. Users should use the Arg template class below -// instead of using this API directly. -SANDBOX_EXPORT BoolExpr - ArgEq(int num, size_t size, uint64_t mask, uint64_t val); - -// Returns the default mask for a system call argument of the specified size. -SANDBOX_EXPORT uint64_t DefaultMask(size_t size); - -} // namespace internal - -template <typename T> -Arg<T>::Arg(int num) - : num_(num), mask_(internal::DefaultMask(sizeof(T))) { -} - -// Definition requires ArgEq to have been declared. Moved out-of-line -// to minimize how much internal clutter users have to ignore while -// reading the header documentation. -// -// Additionally, we use this helper member function to avoid linker errors -// caused by defining operator== out-of-line. For a more detailed explanation, -// see http://www.parashift.com/c++-faq-lite/template-friends.html. -template <typename T> -BoolExpr Arg<T>::EqualTo(T val) const { - if (sizeof(T) == 4) { - // Prevent sign-extension of negative int32_t values. - return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint32_t>(val)); - } - return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint64_t>(val)); -} - -template <typename T> -SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg) { - return Caser<T>(arg, Elser(nullptr)); -} - -template <typename T> -Caser<T> Caser<T>::Case(T value, ResultExpr result) const { - return SANDBOX_BPF_DSL_CASES((value), std::move(result)); -} - -template <typename T> -template <typename... Values> -Caser<T> Caser<T>::CasesImpl(ResultExpr result, const Values&... values) const { - // Theoretically we could evaluate arg_ just once and emit a more efficient - // dispatch table, but for now we simply translate into an equivalent - // If/ElseIf/Else chain. - - return Caser<T>(arg_, - elser_.ElseIf(AnyOf((arg_ == values)...), std::move(result))); -} - -template <typename T> -ResultExpr Caser<T>::Default(ResultExpr result) const { - return elser_.Else(std::move(result)); -} - -template <typename... Rest> -BoolExpr AllOf(BoolExpr first, Rest&&... rest) { - return AllOf(std::move(first), AllOf(std::forward<Rest>(rest)...)); -} - -template <typename... Rest> -BoolExpr AnyOf(BoolExpr first, Rest&&... rest) { - return AnyOf(std::move(first), AnyOf(std::forward<Rest>(rest)...)); -} - -} // namespace bpf_dsl -} // namespace sandbox - -#endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_ diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_forward.h b/sandbox/linux/bpf_dsl/bpf_dsl_forward.h deleted file mode 100644 index af1b48b407..0000000000 --- a/sandbox/linux/bpf_dsl/bpf_dsl_forward.h +++ /dev/null @@ -1,37 +0,0 @@ -// 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. - -#ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_FORWARD_H_ -#define SANDBOX_LINUX_BPF_DSL_BPF_DSL_FORWARD_H_ - -#include <memory> - -#include "sandbox/sandbox_export.h" - -namespace sandbox { -namespace bpf_dsl { - -// The bpf_dsl_forward.h header provides forward declarations for the -// types defined in bpf_dsl.h. It's intended for use in user headers -// that need to reference bpf_dsl types, but don't require definitions. - -namespace internal { -class ResultExprImpl; -class BoolExprImpl; -} - -using ResultExpr = std::shared_ptr<const internal::ResultExprImpl>; -using BoolExpr = std::shared_ptr<const internal::BoolExprImpl>; - -} // namespace bpf_dsl -} // namespace sandbox - -namespace std { -extern template class SANDBOX_EXPORT - shared_ptr<const sandbox::bpf_dsl::internal::BoolExprImpl>; -extern template class SANDBOX_EXPORT - shared_ptr<const sandbox::bpf_dsl::internal::ResultExprImpl>; -} // namespace std - -#endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_FORWARD_H_ diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_impl.h b/sandbox/linux/bpf_dsl/bpf_dsl_impl.h deleted file mode 100644 index f397321edd..0000000000 --- a/sandbox/linux/bpf_dsl/bpf_dsl_impl.h +++ /dev/null @@ -1,67 +0,0 @@ -// 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. - -#ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_IMPL_H_ -#define SANDBOX_LINUX_BPF_DSL_BPF_DSL_IMPL_H_ - -#include <memory> - -#include "base/macros.h" -#include "sandbox/linux/bpf_dsl/codegen.h" -#include "sandbox/sandbox_export.h" - -namespace sandbox { -namespace bpf_dsl { -class PolicyCompiler; - -namespace internal { - -// Internal interface implemented by BoolExpr implementations. -class BoolExprImpl { - public: - // Compile uses |pc| to emit a CodeGen::Node that conditionally continues - // to either |then_node| or |false_node|, depending on whether the represented - // boolean expression is true or false. - virtual CodeGen::Node Compile(PolicyCompiler* pc, - CodeGen::Node then_node, - CodeGen::Node else_node) const = 0; - - protected: - BoolExprImpl() {} - virtual ~BoolExprImpl() {} - - private: - DISALLOW_COPY_AND_ASSIGN(BoolExprImpl); -}; - -// Internal interface implemented by ResultExpr implementations. -class ResultExprImpl { - public: - // Compile uses |pc| to emit a CodeGen::Node that executes the - // represented result expression. - virtual CodeGen::Node Compile(PolicyCompiler* pc) const = 0; - - // HasUnsafeTraps returns whether the result expression is or recursively - // contains an unsafe trap expression. - virtual bool HasUnsafeTraps() const; - - // IsAllow returns whether the result expression is an "allow" result. - virtual bool IsAllow() const; - - // IsAllow returns whether the result expression is a "deny" result. - virtual bool IsDeny() const; - - protected: - ResultExprImpl() {} - virtual ~ResultExprImpl() {} - - private: - DISALLOW_COPY_AND_ASSIGN(ResultExprImpl); -}; - -} // namespace internal -} // namespace bpf_dsl -} // namespace sandbox - -#endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_IMPL_H_ diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc b/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc deleted file mode 100644 index 801deee3e7..0000000000 --- a/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc +++ /dev/null @@ -1,478 +0,0 @@ -// 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 <errno.h> -#include <fcntl.h> -#include <netinet/in.h> -#include <stdint.h> -#include <sys/socket.h> -#include <sys/syscall.h> -#include <sys/utsname.h> -#include <unistd.h> - -#include <map> -#include <utility> - -#include "base/files/scoped_file.h" -#include "base/macros.h" -#include "build/build_config.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" -#include "sandbox/linux/bpf_dsl/codegen.h" -#include "sandbox/linux/bpf_dsl/dump_bpf.h" -#include "sandbox/linux/bpf_dsl/golden/golden_files.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/test_trap_registry.h" -#include "sandbox/linux/bpf_dsl/verifier.h" -#include "sandbox/linux/system_headers/linux_filter.h" -#include "testing/gtest/include/gtest/gtest.h" - -#define CASES SANDBOX_BPF_DSL_CASES - -namespace sandbox { -namespace bpf_dsl { -namespace { - -// Helper function to construct fake arch_seccomp_data objects. -struct arch_seccomp_data FakeSyscall(int nr, - uintptr_t p0 = 0, - uintptr_t p1 = 0, - uintptr_t p2 = 0, - uintptr_t p3 = 0, - uintptr_t p4 = 0, - uintptr_t p5 = 0) { - // Made up program counter for syscall address. - const uint64_t kFakePC = 0x543210; - - struct arch_seccomp_data data = { - nr, - SECCOMP_ARCH, - kFakePC, - { - p0, p1, p2, p3, p4, p5, - }, - }; - - return data; -} - -class PolicyEmulator { - public: - PolicyEmulator(const golden::Golden& golden, const Policy& policy) - : program_() { - TestTrapRegistry traps; - program_ = PolicyCompiler(&policy, &traps).Compile(); - - // TODO(mdempsky): Generalize to more arches. - const char* expected = nullptr; -#if defined(ARCH_CPU_X86) - expected = golden.i386_dump; -#elif defined(ARCH_CPU_X86_64) - expected = golden.x86_64_dump; -#endif - - if (expected != nullptr) { - const std::string actual = DumpBPF::StringPrintProgram(program_); - EXPECT_EQ(expected, actual); - } else { - LOG(WARNING) << "Missing golden file data entry"; - } - } - - ~PolicyEmulator() {} - - void ExpectAllow(const struct arch_seccomp_data& data) const { - EXPECT_EQ(SECCOMP_RET_ALLOW, Emulate(data)); - } - - void ExpectErrno(uint16_t err, const struct arch_seccomp_data& data) const { - EXPECT_EQ(SECCOMP_RET_ERRNO | err, Emulate(data)); - } - - void ExpectKill(const struct arch_seccomp_data& data) const { - EXPECT_EQ(SECCOMP_RET_KILL, Emulate(data)); - } - - private: - uint32_t Emulate(const struct arch_seccomp_data& data) const { - const char* err = nullptr; - uint32_t res = Verifier::EvaluateBPF(program_, data, &err); - if (err) { - ADD_FAILURE() << err; - return 0; - } - return res; - } - - CodeGen::Program program_; - - DISALLOW_COPY_AND_ASSIGN(PolicyEmulator); -}; - -class BasicPolicy : public Policy { - public: - BasicPolicy() {} - ~BasicPolicy() override {} - ResultExpr EvaluateSyscall(int sysno) const override { - if (sysno == __NR_getpgid) { - const Arg<pid_t> pid(0); - return If(pid == 0, Error(EPERM)).Else(Error(EINVAL)); - } - if (sysno == __NR_setuid) { - const Arg<uid_t> uid(0); - return If(uid != 42, Kill()).Else(Allow()); - } - return Allow(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(BasicPolicy); -}; - -TEST(BPFDSL, Basic) { - PolicyEmulator emulator(golden::kBasicPolicy, BasicPolicy()); - - emulator.ExpectErrno(EPERM, FakeSyscall(__NR_getpgid, 0)); - emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_getpgid, 1)); - - emulator.ExpectAllow(FakeSyscall(__NR_setuid, 42)); - emulator.ExpectKill(FakeSyscall(__NR_setuid, 43)); -} - -/* On IA-32, socketpair() is implemented via socketcall(). :-( */ -#if !defined(ARCH_CPU_X86) -class BooleanLogicPolicy : public Policy { - public: - BooleanLogicPolicy() {} - ~BooleanLogicPolicy() override {} - ResultExpr EvaluateSyscall(int sysno) const override { - if (sysno == __NR_socketpair) { - const Arg<int> domain(0), type(1), protocol(2); - return If(AllOf(domain == AF_UNIX, - AnyOf(type == SOCK_STREAM, type == SOCK_DGRAM), - protocol == 0), - Error(EPERM)) - .Else(Error(EINVAL)); - } - return Allow(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(BooleanLogicPolicy); -}; - -TEST(BPFDSL, BooleanLogic) { - PolicyEmulator emulator(golden::kBooleanLogicPolicy, BooleanLogicPolicy()); - - const intptr_t kFakeSV = 0x12345; - - // Acceptable combinations that should return EPERM. - emulator.ExpectErrno( - EPERM, FakeSyscall(__NR_socketpair, AF_UNIX, SOCK_STREAM, 0, kFakeSV)); - emulator.ExpectErrno( - EPERM, FakeSyscall(__NR_socketpair, AF_UNIX, SOCK_DGRAM, 0, kFakeSV)); - - // Combinations that are invalid for only one reason; should return EINVAL. - emulator.ExpectErrno( - EINVAL, FakeSyscall(__NR_socketpair, AF_INET, SOCK_STREAM, 0, kFakeSV)); - emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_socketpair, AF_UNIX, - SOCK_SEQPACKET, 0, kFakeSV)); - emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_socketpair, AF_UNIX, - SOCK_STREAM, IPPROTO_TCP, kFakeSV)); - - // Completely unacceptable combination; should also return EINVAL. - emulator.ExpectErrno( - EINVAL, FakeSyscall(__NR_socketpair, AF_INET, SOCK_SEQPACKET, IPPROTO_UDP, - kFakeSV)); -} -#endif // !ARCH_CPU_X86 - -class MoreBooleanLogicPolicy : public Policy { - public: - MoreBooleanLogicPolicy() {} - ~MoreBooleanLogicPolicy() override {} - ResultExpr EvaluateSyscall(int sysno) const override { - if (sysno == __NR_setresuid) { - const Arg<uid_t> ruid(0), euid(1), suid(2); - return If(AnyOf(ruid == 0, euid == 0, suid == 0), Error(EPERM)) - .ElseIf(AllOf(ruid == 1, euid == 1, suid == 1), Error(EAGAIN)) - .Else(Error(EINVAL)); - } - return Allow(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(MoreBooleanLogicPolicy); -}; - -TEST(BPFDSL, MoreBooleanLogic) { - PolicyEmulator emulator(golden::kMoreBooleanLogicPolicy, - MoreBooleanLogicPolicy()); - - // Expect EPERM if any set to 0. - emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 0, 5, 5)); - emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 5, 0, 5)); - emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 5, 5, 0)); - - // Expect EAGAIN if all set to 1. - emulator.ExpectErrno(EAGAIN, FakeSyscall(__NR_setresuid, 1, 1, 1)); - - // Expect EINVAL for anything else. - emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 5, 1, 1)); - emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 1, 5, 1)); - emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 1, 1, 5)); - emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 3, 4, 5)); -} - -static const uintptr_t kDeadBeefAddr = - static_cast<uintptr_t>(0xdeadbeefdeadbeefULL); - -class ArgSizePolicy : public Policy { - public: - ArgSizePolicy() {} - ~ArgSizePolicy() override {} - ResultExpr EvaluateSyscall(int sysno) const override { - if (sysno == __NR_uname) { - const Arg<uintptr_t> addr(0); - return If(addr == kDeadBeefAddr, Error(EPERM)).Else(Allow()); - } - return Allow(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(ArgSizePolicy); -}; - -TEST(BPFDSL, ArgSizeTest) { - PolicyEmulator emulator(golden::kArgSizePolicy, ArgSizePolicy()); - - emulator.ExpectAllow(FakeSyscall(__NR_uname, 0)); - emulator.ExpectErrno(EPERM, FakeSyscall(__NR_uname, kDeadBeefAddr)); -} - -class NegativeConstantsPolicy : public Policy { - public: - NegativeConstantsPolicy() {} - ~NegativeConstantsPolicy() override {} - ResultExpr EvaluateSyscall(int sysno) const override { - if (sysno == __NR_fcntl) { - const Arg<int> fd(0); - return If(fd == -314, Error(EPERM)).Else(Allow()); - } - return Allow(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(NegativeConstantsPolicy); -}; - -TEST(BPFDSL, NegativeConstantsTest) { - PolicyEmulator emulator(golden::kNegativeConstantsPolicy, - NegativeConstantsPolicy()); - - emulator.ExpectAllow(FakeSyscall(__NR_fcntl, -5, F_DUPFD)); - emulator.ExpectAllow(FakeSyscall(__NR_fcntl, 20, F_DUPFD)); - emulator.ExpectErrno(EPERM, FakeSyscall(__NR_fcntl, -314, F_DUPFD)); -} - -#if 0 -// TODO(mdempsky): This is really an integration test. - -class TrappingPolicy : public Policy { - public: - TrappingPolicy() {} - ~TrappingPolicy() override {} - ResultExpr EvaluateSyscall(int sysno) const override { - if (sysno == __NR_uname) { - return Trap(UnameTrap, &count_); - } - return Allow(); - } - - private: - static intptr_t count_; - - static intptr_t UnameTrap(const struct arch_seccomp_data& data, void* aux) { - BPF_ASSERT_EQ(&count_, aux); - return ++count_; - } - - DISALLOW_COPY_AND_ASSIGN(TrappingPolicy); -}; - -intptr_t TrappingPolicy::count_; - -BPF_TEST_C(BPFDSL, TrapTest, TrappingPolicy) { - ASSERT_SYSCALL_RESULT(1, uname, NULL); - ASSERT_SYSCALL_RESULT(2, uname, NULL); - ASSERT_SYSCALL_RESULT(3, uname, NULL); -} -#endif - -class MaskingPolicy : public Policy { - public: - MaskingPolicy() {} - ~MaskingPolicy() override {} - ResultExpr EvaluateSyscall(int sysno) const override { - if (sysno == __NR_setuid) { - const Arg<uid_t> uid(0); - return If((uid & 0xf) == 0, Error(EINVAL)).Else(Error(EACCES)); - } - if (sysno == __NR_setgid) { - const Arg<gid_t> gid(0); - return If((gid & 0xf0) == 0xf0, Error(EINVAL)).Else(Error(EACCES)); - } - if (sysno == __NR_setpgid) { - const Arg<pid_t> pid(0); - return If((pid & 0xa5) == 0xa0, Error(EINVAL)).Else(Error(EACCES)); - } - return Allow(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(MaskingPolicy); -}; - -TEST(BPFDSL, MaskTest) { - PolicyEmulator emulator(golden::kMaskingPolicy, MaskingPolicy()); - - for (uid_t uid = 0; uid < 0x100; ++uid) { - const int expect_errno = (uid & 0xf) == 0 ? EINVAL : EACCES; - emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setuid, uid)); - } - - for (gid_t gid = 0; gid < 0x100; ++gid) { - const int expect_errno = (gid & 0xf0) == 0xf0 ? EINVAL : EACCES; - emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setgid, gid)); - } - - for (pid_t pid = 0; pid < 0x100; ++pid) { - const int expect_errno = (pid & 0xa5) == 0xa0 ? EINVAL : EACCES; - emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setpgid, pid, 0)); - } -} - -class ElseIfPolicy : public Policy { - public: - ElseIfPolicy() {} - ~ElseIfPolicy() override {} - ResultExpr EvaluateSyscall(int sysno) const override { - if (sysno == __NR_setuid) { - const Arg<uid_t> uid(0); - return If((uid & 0xfff) == 0, Error(0)) - .ElseIf((uid & 0xff0) == 0, Error(EINVAL)) - .ElseIf((uid & 0xf00) == 0, Error(EEXIST)) - .Else(Error(EACCES)); - } - return Allow(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(ElseIfPolicy); -}; - -TEST(BPFDSL, ElseIfTest) { - PolicyEmulator emulator(golden::kElseIfPolicy, ElseIfPolicy()); - - emulator.ExpectErrno(0, FakeSyscall(__NR_setuid, 0)); - - emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setuid, 0x0001)); - emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setuid, 0x0002)); - - emulator.ExpectErrno(EEXIST, FakeSyscall(__NR_setuid, 0x0011)); - emulator.ExpectErrno(EEXIST, FakeSyscall(__NR_setuid, 0x0022)); - - emulator.ExpectErrno(EACCES, FakeSyscall(__NR_setuid, 0x0111)); - emulator.ExpectErrno(EACCES, FakeSyscall(__NR_setuid, 0x0222)); -} - -class SwitchPolicy : public Policy { - public: - SwitchPolicy() {} - ~SwitchPolicy() override {} - ResultExpr EvaluateSyscall(int sysno) const override { - if (sysno == __NR_fcntl) { - const Arg<int> cmd(1); - const Arg<unsigned long> long_arg(2); - return Switch(cmd) - .CASES((F_GETFL, F_GETFD), Error(ENOENT)) - .Case(F_SETFD, If(long_arg == O_CLOEXEC, Allow()).Else(Error(EINVAL))) - .Case(F_SETFL, Error(EPERM)) - .Default(Error(EACCES)); - } - return Allow(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(SwitchPolicy); -}; - -TEST(BPFDSL, SwitchTest) { - PolicyEmulator emulator(golden::kSwitchPolicy, SwitchPolicy()); - - const int kFakeSockFD = 42; - - emulator.ExpectErrno(ENOENT, FakeSyscall(__NR_fcntl, kFakeSockFD, F_GETFD)); - emulator.ExpectErrno(ENOENT, FakeSyscall(__NR_fcntl, kFakeSockFD, F_GETFL)); - - emulator.ExpectAllow( - FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFD, O_CLOEXEC)); - emulator.ExpectErrno(EINVAL, - FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFD, 0)); - - emulator.ExpectErrno(EPERM, - FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFL, O_RDONLY)); - - emulator.ExpectErrno(EACCES, - FakeSyscall(__NR_fcntl, kFakeSockFD, F_DUPFD, 0)); -} - -static intptr_t DummyTrap(const struct arch_seccomp_data& data, void* aux) { - return 0; -} - -TEST(BPFDSL, IsAllowDeny) { - ResultExpr allow = Allow(); - EXPECT_TRUE(allow->IsAllow()); - EXPECT_FALSE(allow->IsDeny()); - - ResultExpr error = Error(ENOENT); - EXPECT_FALSE(error->IsAllow()); - EXPECT_TRUE(error->IsDeny()); - - ResultExpr trace = Trace(42); - EXPECT_FALSE(trace->IsAllow()); - EXPECT_FALSE(trace->IsDeny()); - - ResultExpr trap = Trap(DummyTrap, nullptr); - EXPECT_FALSE(trap->IsAllow()); - EXPECT_TRUE(trap->IsDeny()); - - const Arg<int> arg(0); - ResultExpr maybe = If(arg == 0, Allow()).Else(Error(EPERM)); - EXPECT_FALSE(maybe->IsAllow()); - EXPECT_FALSE(maybe->IsDeny()); -} - -TEST(BPFDSL, HasUnsafeTraps) { - ResultExpr allow = Allow(); - EXPECT_FALSE(allow->HasUnsafeTraps()); - - ResultExpr safe = Trap(DummyTrap, nullptr); - EXPECT_FALSE(safe->HasUnsafeTraps()); - - ResultExpr unsafe = UnsafeTrap(DummyTrap, nullptr); - EXPECT_TRUE(unsafe->HasUnsafeTraps()); - - const Arg<int> arg(0); - ResultExpr maybe = If(arg == 0, allow).Else(unsafe); - EXPECT_TRUE(maybe->HasUnsafeTraps()); -} - -} // namespace -} // namespace bpf_dsl -} // namespace sandbox diff --git a/sandbox/linux/bpf_dsl/codegen.cc b/sandbox/linux/bpf_dsl/codegen.cc deleted file mode 100644 index d88bd531a2..0000000000 --- a/sandbox/linux/bpf_dsl/codegen.cc +++ /dev/null @@ -1,147 +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/bpf_dsl/codegen.h" - -#include <stddef.h> -#include <stdint.h> - -#include <limits> -#include <utility> - -#include "base/logging.h" -#include "sandbox/linux/system_headers/linux_filter.h" - -// This CodeGen implementation strives for simplicity while still -// generating acceptable BPF programs under typical usage patterns -// (e.g., by PolicyCompiler). -// -// The key to its simplicity is that BPF programs only support forward -// jumps/branches, which allows constraining the DAG construction API -// to make instruction nodes immutable. Immutable nodes admits a -// simple greedy approach of emitting new instructions as needed and -// then reusing existing ones that have already been emitted. This -// cleanly avoids any need to compute basic blocks or apply -// topological sorting because the API effectively sorts instructions -// for us (e.g., before MakeInstruction() can be called to emit a -// branch instruction, it must have already been called for each -// branch path). -// -// This greedy algorithm is not without (theoretical) weakness though: -// -// 1. In the general case, we don't eliminate dead code. If needed, -// we could trace back through the program in Compile() and elide -// any unneeded instructions, but in practice we only emit live -// instructions anyway. -// -// 2. By not dividing instructions into basic blocks and sorting, we -// lose an opportunity to move non-branch/non-return instructions -// adjacent to their successor instructions, which means we might -// need to emit additional jumps. But in practice, they'll -// already be nearby as long as callers don't go out of their way -// to interleave MakeInstruction() calls for unrelated code -// sequences. - -namespace sandbox { - -// kBranchRange is the maximum value that can be stored in -// sock_filter's 8-bit jt and jf fields. -const size_t kBranchRange = std::numeric_limits<uint8_t>::max(); - -const CodeGen::Node CodeGen::kNullNode; - -CodeGen::CodeGen() : program_(), equivalent_(), memos_() { -} - -CodeGen::~CodeGen() { -} - -CodeGen::Program CodeGen::Compile(CodeGen::Node head) { - return Program(program_.rbegin() + Offset(head), program_.rend()); -} - -CodeGen::Node CodeGen::MakeInstruction(uint16_t code, - uint32_t k, - Node jt, - Node jf) { - // To avoid generating redundant code sequences, we memoize the - // results from AppendInstruction(). - auto res = memos_.insert(std::make_pair(MemoKey(code, k, jt, jf), kNullNode)); - CodeGen::Node* node = &res.first->second; - if (res.second) { // Newly inserted memo entry. - *node = AppendInstruction(code, k, jt, jf); - } - return *node; -} - -CodeGen::Node CodeGen::AppendInstruction(uint16_t code, - uint32_t k, - Node jt, - Node jf) { - if (BPF_CLASS(code) == BPF_JMP) { - CHECK_NE(BPF_JA, BPF_OP(code)) << "CodeGen inserts JAs as needed"; - - // Optimally adding jumps is rather tricky, so we use a quick - // approximation: by artificially reducing |jt|'s range, |jt| will - // stay within its true range even if we add a jump for |jf|. - jt = WithinRange(jt, kBranchRange - 1); - jf = WithinRange(jf, kBranchRange); - return Append(code, k, Offset(jt), Offset(jf)); - } - - CHECK_EQ(kNullNode, jf) << "Non-branch instructions shouldn't provide jf"; - if (BPF_CLASS(code) == BPF_RET) { - CHECK_EQ(kNullNode, jt) << "Return instructions shouldn't provide jt"; - } else { - // For non-branch/non-return instructions, execution always - // proceeds to the next instruction; so we need to arrange for - // that to be |jt|. - jt = WithinRange(jt, 0); - CHECK_EQ(0U, Offset(jt)) << "ICE: Failed to setup next instruction"; - } - return Append(code, k, 0, 0); -} - -CodeGen::Node CodeGen::WithinRange(Node target, size_t range) { - // Just use |target| if it's already within range. - if (Offset(target) <= range) { - return target; - } - - // Alternatively, look for an equivalent instruction within range. - if (Offset(equivalent_.at(target)) <= range) { - return equivalent_.at(target); - } - - // Otherwise, fall back to emitting a jump instruction. - Node jump = Append(BPF_JMP | BPF_JA, Offset(target), 0, 0); - equivalent_.at(target) = jump; - return jump; -} - -CodeGen::Node CodeGen::Append(uint16_t code, uint32_t k, size_t jt, size_t jf) { - if (BPF_CLASS(code) == BPF_JMP && BPF_OP(code) != BPF_JA) { - CHECK_LE(jt, kBranchRange); - CHECK_LE(jf, kBranchRange); - } else { - CHECK_EQ(0U, jt); - CHECK_EQ(0U, jf); - } - - CHECK_LT(program_.size(), static_cast<size_t>(BPF_MAXINSNS)); - CHECK_EQ(program_.size(), equivalent_.size()); - - Node res = program_.size(); - program_.push_back(sock_filter{ - code, static_cast<uint8_t>(jt), static_cast<uint8_t>(jf), k}); - equivalent_.push_back(res); - return res; -} - -size_t CodeGen::Offset(Node target) const { - CHECK_LT(target, program_.size()) << "Bogus offset target node"; - return (program_.size() - 1) - target; -} - -} // namespace sandbox diff --git a/sandbox/linux/bpf_dsl/codegen.h b/sandbox/linux/bpf_dsl/codegen.h deleted file mode 100644 index 3fc3f35a0d..0000000000 --- a/sandbox/linux/bpf_dsl/codegen.h +++ /dev/null @@ -1,119 +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. - -#ifndef SANDBOX_LINUX_BPF_DSL_CODEGEN_H__ -#define SANDBOX_LINUX_BPF_DSL_CODEGEN_H__ - -#include <stddef.h> -#include <stdint.h> - -#include <map> -#include <tuple> -#include <vector> - -#include "base/macros.h" -#include "sandbox/sandbox_export.h" - -struct sock_filter; - -namespace sandbox { - -// The code generator implements a basic assembler that can convert a -// graph of BPF instructions into a well-formed array of BPF -// instructions. Most notably, it ensures that jumps are always -// forward and don't exceed the limit of 255 instructions imposed by -// the instruction set. -// -// Callers would typically create a new CodeGen object and then use it -// to build a DAG of instruction nodes. They'll eventually call -// Compile() to convert this DAG to a Program. -// -// CodeGen gen; -// CodeGen::Node allow, branch, dag; -// -// allow = -// gen.MakeInstruction(BPF_RET+BPF_K, -// ErrorCode(ErrorCode::ERR_ALLOWED).err())); -// branch = -// gen.MakeInstruction(BPF_JMP+BPF_EQ+BPF_K, __NR_getpid, -// Trap(GetPidHandler, NULL), allow); -// dag = -// gen.MakeInstruction(BPF_LD+BPF_W+BPF_ABS, -// offsetof(struct arch_seccomp_data, nr), branch); -// -// // Simplified code follows; in practice, it is important to avoid calling -// // any C++ destructors after starting the sandbox. -// CodeGen::Program program = gen.Compile(dag); -// const struct sock_fprog prog = { -// static_cast<unsigned short>(program.size()), &program[0] }; -// prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); -// -class SANDBOX_EXPORT CodeGen { - public: - // A vector of BPF instructions that need to be installed as a filter - // program in the kernel. - typedef std::vector<struct sock_filter> Program; - - // Node represents a node within the instruction DAG being compiled. - using Node = Program::size_type; - - // kNullNode represents the "null" node; i.e., the reserved node - // value guaranteed to not equal any actual nodes. - static const Node kNullNode = -1; - - CodeGen(); - ~CodeGen(); - - // MakeInstruction creates a node representing the specified - // instruction, or returns and existing equivalent node if one - // exists. For details on the possible parameters refer to - // https://www.kernel.org/doc/Documentation/networking/filter.txt. - // TODO(mdempsky): Reconsider using default arguments here. - Node MakeInstruction(uint16_t code, - uint32_t k, - Node jt = kNullNode, - Node jf = kNullNode); - - // Compile linearizes the instruction DAG rooted at |head| into a - // program that can be executed by a BPF virtual machine. - Program Compile(Node head); - - private: - using MemoKey = std::tuple<uint16_t, uint32_t, Node, Node>; - - // AppendInstruction adds a new instruction, ensuring that |jt| and - // |jf| are within range as necessary for |code|. - Node AppendInstruction(uint16_t code, uint32_t k, Node jt, Node jf); - - // WithinRange returns a node equivalent to |next| that is at most - // |range| instructions away from the (logical) beginning of the - // program. - Node WithinRange(Node next, size_t range); - - // Append appends a new instruction to the physical end (i.e., - // logical beginning) of |program_|. - Node Append(uint16_t code, uint32_t k, size_t jt, size_t jf); - - // Offset returns how many instructions exist in |program_| after |target|. - size_t Offset(Node target) const; - - // NOTE: program_ is the compiled program in *reverse*, so that - // indices remain stable as we add instructions. - Program program_; - - // equivalent_ stores the most recent semantically-equivalent node for each - // instruction in program_. A node is defined as semantically-equivalent to N - // if it has the same instruction code and constant as N and its successor - // nodes (if any) are semantically-equivalent to N's successor nodes, or - // if it's an unconditional jump to a node semantically-equivalent to N. - std::vector<Node> equivalent_; - - std::map<MemoKey, Node> memos_; - - DISALLOW_COPY_AND_ASSIGN(CodeGen); -}; - -} // namespace sandbox - -#endif // SANDBOX_LINUX_BPF_DSL_CODEGEN_H__ diff --git a/sandbox/linux/bpf_dsl/codegen_unittest.cc b/sandbox/linux/bpf_dsl/codegen_unittest.cc deleted file mode 100644 index 56a0dd27e1..0000000000 --- a/sandbox/linux/bpf_dsl/codegen_unittest.cc +++ /dev/null @@ -1,404 +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/bpf_dsl/codegen.h" - -#include <stddef.h> -#include <stdint.h> - -#include <map> -#include <utility> -#include <vector> - -#include "base/macros.h" -#include "base/md5.h" -#include "base/strings/string_piece.h" -#include "sandbox/linux/system_headers/linux_filter.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { -namespace { - -// Hash provides an abstraction for building "hash trees" from BPF -// control flow graphs, and efficiently identifying equivalent graphs. -// -// For simplicity, we use MD5, because base happens to provide a -// convenient API for its use. However, any collision-resistant hash -// should suffice. -class Hash { - public: - static const Hash kZero; - - Hash() : digest_() {} - - Hash(uint16_t code, - uint32_t k, - const Hash& jt = kZero, - const Hash& jf = kZero) - : digest_() { - base::MD5Context ctx; - base::MD5Init(&ctx); - HashValue(&ctx, code); - HashValue(&ctx, k); - HashValue(&ctx, jt); - HashValue(&ctx, jf); - base::MD5Final(&digest_, &ctx); - } - - Hash(const Hash& hash) = default; - Hash& operator=(const Hash& rhs) = default; - - friend bool operator==(const Hash& lhs, const Hash& rhs) { - return lhs.Base16() == rhs.Base16(); - } - friend bool operator!=(const Hash& lhs, const Hash& rhs) { - return !(lhs == rhs); - } - - private: - template <typename T> - void HashValue(base::MD5Context* ctx, const T& value) { - base::MD5Update(ctx, - base::StringPiece(reinterpret_cast<const char*>(&value), - sizeof(value))); - } - - std::string Base16() const { - return base::MD5DigestToBase16(digest_); - } - - base::MD5Digest digest_; -}; - -const Hash Hash::kZero; - -// Sanity check that equality and inequality work on Hash as required. -TEST(CodeGen, HashSanity) { - std::vector<Hash> hashes; - - // Push a bunch of logically distinct hashes. - hashes.push_back(Hash::kZero); - for (int i = 0; i < 4; ++i) { - hashes.push_back(Hash(i & 1, i & 2)); - } - for (int i = 0; i < 16; ++i) { - hashes.push_back(Hash(i & 1, i & 2, Hash(i & 4, i & 8))); - } - for (int i = 0; i < 64; ++i) { - hashes.push_back( - Hash(i & 1, i & 2, Hash(i & 4, i & 8), Hash(i & 16, i & 32))); - } - - for (const Hash& a : hashes) { - for (const Hash& b : hashes) { - // Hashes should equal themselves, but not equal all others. - if (&a == &b) { - EXPECT_EQ(a, b); - } else { - EXPECT_NE(a, b); - } - } - } -} - -// ProgramTest provides a fixture for writing compiling sample -// programs with CodeGen and verifying the linearized output matches -// the input DAG. -class ProgramTest : public ::testing::Test { - protected: - ProgramTest() : gen_(), node_hashes_() {} - - // MakeInstruction calls CodeGen::MakeInstruction() and associated - // the returned address with a hash of the instruction. - CodeGen::Node MakeInstruction(uint16_t code, - uint32_t k, - CodeGen::Node jt = CodeGen::kNullNode, - CodeGen::Node jf = CodeGen::kNullNode) { - CodeGen::Node res = gen_.MakeInstruction(code, k, jt, jf); - EXPECT_NE(CodeGen::kNullNode, res); - - Hash digest(code, k, Lookup(jt), Lookup(jf)); - auto it = node_hashes_.insert(std::make_pair(res, digest)); - EXPECT_EQ(digest, it.first->second); - - return res; - } - - // RunTest compiles the program and verifies that the output matches - // what is expected. It should be called at the end of each program - // test case. - void RunTest(CodeGen::Node head) { - // Compile the program - CodeGen::Program program = gen_.Compile(head); - - // Walk the program backwards, and compute the hash for each instruction. - std::vector<Hash> prog_hashes(program.size()); - for (size_t i = program.size(); i > 0; --i) { - const sock_filter& insn = program.at(i - 1); - Hash& hash = prog_hashes.at(i - 1); - - if (BPF_CLASS(insn.code) == BPF_JMP) { - if (BPF_OP(insn.code) == BPF_JA) { - // The compiler adds JA instructions as needed, so skip them. - hash = prog_hashes.at(i + insn.k); - } else { - hash = Hash(insn.code, insn.k, prog_hashes.at(i + insn.jt), - prog_hashes.at(i + insn.jf)); - } - } else if (BPF_CLASS(insn.code) == BPF_RET) { - hash = Hash(insn.code, insn.k); - } else { - hash = Hash(insn.code, insn.k, prog_hashes.at(i)); - } - } - - EXPECT_EQ(Lookup(head), prog_hashes.at(0)); - } - - private: - const Hash& Lookup(CodeGen::Node next) const { - if (next == CodeGen::kNullNode) { - return Hash::kZero; - } - auto it = node_hashes_.find(next); - if (it == node_hashes_.end()) { - ADD_FAILURE() << "No hash found for node " << next; - return Hash::kZero; - } - return it->second; - } - - CodeGen gen_; - std::map<CodeGen::Node, Hash> node_hashes_; - - DISALLOW_COPY_AND_ASSIGN(ProgramTest); -}; - -TEST_F(ProgramTest, OneInstruction) { - // Create the most basic valid BPF program: - // RET 0 - CodeGen::Node head = MakeInstruction(BPF_RET + BPF_K, 0); - RunTest(head); -} - -TEST_F(ProgramTest, SimpleBranch) { - // Create a program with a single branch: - // JUMP if eq 42 then $0 else $1 - // 0: RET 1 - // 1: RET 0 - CodeGen::Node head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, - MakeInstruction(BPF_RET + BPF_K, 1), - MakeInstruction(BPF_RET + BPF_K, 0)); - RunTest(head); -} - -TEST_F(ProgramTest, AtypicalBranch) { - // Create a program with a single branch: - // JUMP if eq 42 then $0 else $0 - // 0: RET 0 - - CodeGen::Node ret = MakeInstruction(BPF_RET + BPF_K, 0); - CodeGen::Node head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, ret, ret); - - // N.B.: As the instructions in both sides of the branch are already - // the same object, we do not actually have any "mergeable" branches. - // This needs to be reflected in our choice of "flags". - RunTest(head); -} - -TEST_F(ProgramTest, Complex) { - // Creates a basic BPF program that we'll use to test some of the code: - // JUMP if eq 42 the $0 else $1 (insn6) - // 0: LD 23 (insn5) - // 1: JUMP if eq 42 then $2 else $4 (insn4) - // 2: JUMP to $3 (insn2) - // 3: LD 42 (insn1) - // RET 42 (insn0) - // 4: LD 42 (insn3) - // RET 42 (insn3+) - CodeGen::Node insn0 = MakeInstruction(BPF_RET + BPF_K, 42); - CodeGen::Node insn1 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, insn0); - CodeGen::Node insn2 = insn1; // Implicit JUMP - - // We explicitly duplicate instructions to test that they're merged. - CodeGen::Node insn3 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, - MakeInstruction(BPF_RET + BPF_K, 42)); - EXPECT_EQ(insn2, insn3); - - CodeGen::Node insn4 = - MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn2, insn3); - CodeGen::Node insn5 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 23, insn4); - - // Force a basic block that ends in neither a jump instruction nor a return - // instruction. It only contains "insn5". This exercises one of the less - // common code paths in the topo-sort algorithm. - // This also gives us a diamond-shaped pattern in our graph, which stresses - // another aspect of the topo-sort algorithm (namely, the ability to - // correctly count the incoming branches for subtrees that are not disjunct). - CodeGen::Node insn6 = - MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn5, insn4); - - RunTest(insn6); -} - -TEST_F(ProgramTest, ConfusingTails) { - // This simple program demonstrates https://crbug.com/351103/ - // The two "LOAD 0" instructions are blocks of their own. MergeTails() could - // be tempted to merge them since they are the same. However, they are - // not mergeable because they fall-through to non semantically equivalent - // blocks. - // Without the fix for this bug, this program should trigger the check in - // CompileAndCompare: the serialized graphs from the program and its compiled - // version will differ. - // - // 0) LOAD 1 // ??? - // 1) if A == 0x1; then JMP 2 else JMP 3 - // 2) LOAD 0 // System call number - // 3) if A == 0x2; then JMP 4 else JMP 5 - // 4) LOAD 0 // System call number - // 5) if A == 0x1; then JMP 6 else JMP 7 - // 6) RET 0 - // 7) RET 1 - - CodeGen::Node i7 = MakeInstruction(BPF_RET + BPF_K, 1); - CodeGen::Node i6 = MakeInstruction(BPF_RET + BPF_K, 0); - CodeGen::Node i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7); - CodeGen::Node i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5); - CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5); - CodeGen::Node i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3); - CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3); - CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1); - - RunTest(i0); -} - -TEST_F(ProgramTest, ConfusingTailsBasic) { - // Without the fix for https://crbug.com/351103/, (see - // SampleProgramConfusingTails()), this would generate a cyclic graph and - // crash as the two "LOAD 0" instructions would get merged. - // - // 0) LOAD 1 // ??? - // 1) if A == 0x1; then JMP 2 else JMP 3 - // 2) LOAD 0 // System call number - // 3) if A == 0x2; then JMP 4 else JMP 5 - // 4) LOAD 0 // System call number - // 5) RET 1 - - CodeGen::Node i5 = MakeInstruction(BPF_RET + BPF_K, 1); - CodeGen::Node i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5); - CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5); - CodeGen::Node i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3); - CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3); - CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1); - - RunTest(i0); -} - -TEST_F(ProgramTest, ConfusingTailsMergeable) { - // This is similar to SampleProgramConfusingTails(), except that - // instructions 2 and 4 are now RET instructions. - // In PointerCompare(), this exercises the path where two blocks are of the - // same length and identical and the last instruction is a JMP or RET, so the - // following blocks don't need to be looked at and the blocks are mergeable. - // - // 0) LOAD 1 // ??? - // 1) if A == 0x1; then JMP 2 else JMP 3 - // 2) RET 42 - // 3) if A == 0x2; then JMP 4 else JMP 5 - // 4) RET 42 - // 5) if A == 0x1; then JMP 6 else JMP 7 - // 6) RET 0 - // 7) RET 1 - - CodeGen::Node i7 = MakeInstruction(BPF_RET + BPF_K, 1); - CodeGen::Node i6 = MakeInstruction(BPF_RET + BPF_K, 0); - CodeGen::Node i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7); - CodeGen::Node i4 = MakeInstruction(BPF_RET + BPF_K, 42); - CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5); - CodeGen::Node i2 = MakeInstruction(BPF_RET + BPF_K, 42); - CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3); - CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1); - - RunTest(i0); -} - -TEST_F(ProgramTest, InstructionFolding) { - // Check that simple instructions are folded as expected. - CodeGen::Node a = MakeInstruction(BPF_RET + BPF_K, 0); - EXPECT_EQ(a, MakeInstruction(BPF_RET + BPF_K, 0)); - CodeGen::Node b = MakeInstruction(BPF_RET + BPF_K, 1); - EXPECT_EQ(a, MakeInstruction(BPF_RET + BPF_K, 0)); - EXPECT_EQ(b, MakeInstruction(BPF_RET + BPF_K, 1)); - EXPECT_EQ(b, MakeInstruction(BPF_RET + BPF_K, 1)); - - // Check that complex sequences are folded too. - CodeGen::Node c = - MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, - MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, 0x100, a, b)); - EXPECT_EQ(c, MakeInstruction( - BPF_LD + BPF_W + BPF_ABS, 0, - MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, 0x100, a, b))); - - RunTest(c); -} - -TEST_F(ProgramTest, FarBranches) { - // BPF instructions use 8-bit fields for branch offsets, which means - // branch targets must be within 255 instructions of the branch - // instruction. CodeGen abstracts away this detail by inserting jump - // instructions as needed, which we test here by generating programs - // that should trigger any interesting boundary conditions. - - // Populate with 260 initial instruction nodes. - std::vector<CodeGen::Node> nodes; - nodes.push_back(MakeInstruction(BPF_RET + BPF_K, 0)); - for (size_t i = 1; i < 260; ++i) { - nodes.push_back( - MakeInstruction(BPF_ALU + BPF_ADD + BPF_K, i, nodes.back())); - } - - // Exhaustively test branch offsets near BPF's limits. - for (size_t jt = 250; jt < 260; ++jt) { - for (size_t jf = 250; jf < 260; ++jf) { - nodes.push_back(MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 0, - nodes.rbegin()[jt], nodes.rbegin()[jf])); - RunTest(nodes.back()); - } - } -} - -TEST_F(ProgramTest, JumpReuse) { - // As a code size optimization, we try to reuse jumps when possible - // instead of emitting new ones. Here we make sure that optimization - // is working as intended. - // - // NOTE: To simplify testing, we rely on implementation details - // about what CodeGen::Node values indicate (i.e., vector indices), - // but CodeGen users should treat them as opaque values. - - // Populate with 260 initial instruction nodes. - std::vector<CodeGen::Node> nodes; - nodes.push_back(MakeInstruction(BPF_RET + BPF_K, 0)); - for (size_t i = 1; i < 260; ++i) { - nodes.push_back( - MakeInstruction(BPF_ALU + BPF_ADD + BPF_K, i, nodes.back())); - } - - // Branching to nodes[0] and nodes[1] should require 3 new - // instructions: two far jumps plus the branch itself. - CodeGen::Node one = - MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 0, nodes[0], nodes[1]); - EXPECT_EQ(nodes.back() + 3, one); // XXX: Implementation detail! - RunTest(one); - - // Branching again to the same target nodes should require only one - // new instruction, as we can reuse the previous branch's jumps. - CodeGen::Node two = - MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, nodes[0], nodes[1]); - EXPECT_EQ(one + 1, two); // XXX: Implementation detail! - RunTest(two); -} - -} // namespace -} // namespace sandbox diff --git a/sandbox/linux/bpf_dsl/cons.h b/sandbox/linux/bpf_dsl/cons.h deleted file mode 100644 index 07ac3dfc23..0000000000 --- a/sandbox/linux/bpf_dsl/cons.h +++ /dev/null @@ -1,137 +0,0 @@ -// 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. - -#ifndef SANDBOX_LINUX_BPF_DSL_CONS_H_ -#define SANDBOX_LINUX_BPF_DSL_CONS_H_ - -#include <memory> - -#include "base/macros.h" -#include "sandbox/sandbox_export.h" - -namespace sandbox { -namespace cons { - -// Namespace cons provides an abstraction for immutable "cons list" -// data structures as commonly provided in functional programming -// languages like Lisp or Haskell. -// -// A cons list is a linked list consisting of "cells", each of which -// have a "head" and a "tail" element. A cell's head element contains -// a user specified value, while the tail element contains a (possibly -// null) pointer to another cell. -// -// An empty list (idiomatically referred to as "nil") can be -// constructed as "cons::List<Foo>()" or simply as "nullptr" if Foo -// can be inferred from context (e.g., calling a function that has a -// "cons::List<Foo>" parameter). -// -// Existing lists (including empty lists) can be extended by -// prepending new values to the front using the "Cons(head, tail)" -// function, which will allocate a new cons cell. Notably, cons lists -// support creating multiple lists that share a common tail sequence. -// -// Lastly, lists support iteration via C++11's range-based for loop -// construct. -// -// Examples: -// -// // basic construction -// const cons::List<char> kNil = nullptr; -// cons::List<char> ba = Cons('b', Cons('a', kNil)); -// -// // common tail sequence -// cons::List<char> cba = Cons('c', ba); -// cons::List<char> dba = Cons('d', ba); -// -// // iteration -// for (const char& ch : cba) { -// // iterates 'c', 'b', 'a' -// } -// for (const char& ch : dba) { -// // iterates 'd', 'b', 'a' -// } - -// Forward declarations. -template <typename T> -class Cell; -template <typename T> -class ListIterator; - -// List represents a (possibly null) pointer to a cons cell. -template <typename T> -using List = std::shared_ptr<const Cell<T>>; - -// Cons extends a cons list by prepending a new value to the front. -template <typename T> -List<T> Cons(const T& head, List<T> tail) { - return std::make_shared<Cell<T>>(head, std::move(tail)); -} - -// Cell represents an individual "cons cell" within a cons list. -template <typename T> -class Cell { - public: - Cell(const T& head, List<T> tail) : head_(head), tail_(std::move(tail)) {} - - // Head returns this cell's head element. - const T& head() const { return head_; } - - // Tail returns this cell's tail element. - const List<T>& tail() const { return tail_; } - - private: - T head_; - List<T> tail_; - - DISALLOW_COPY_AND_ASSIGN(Cell); -}; - -// Begin returns a list iterator pointing to the first element of the -// cons list. It's provided to support range-based for loops. -template <typename T> -ListIterator<T> begin(const List<T>& list) { - return ListIterator<T>(list); -} - -// End returns a list iterator pointing to the "past-the-end" element -// of the cons list (i.e., nil). It's provided to support range-based -// for loops. -template <typename T> -ListIterator<T> end(const List<T>& list) { - return ListIterator<T>(); -} - -// ListIterator provides C++ forward iterator semantics for traversing -// a cons list. -template <typename T> -class ListIterator { - public: - ListIterator() : list_() {} - explicit ListIterator(const List<T>& list) : list_(list) {} - - const T& operator*() const { return list_->head(); } - - ListIterator& operator++() { - list_ = list_->tail(); - return *this; - } - - friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) { - return lhs.list_ == rhs.list_; - } - - private: - List<T> list_; -}; - -template <typename T> -bool operator!=(const ListIterator<T>& lhs, const ListIterator<T>& rhs) { - return !(lhs == rhs); -} - -} // namespace cons -} // namespace sandbox - -#endif // SANDBOX_LINUX_BPF_DSL_CONS_H_ diff --git a/sandbox/linux/bpf_dsl/dump_bpf.cc b/sandbox/linux/bpf_dsl/dump_bpf.cc deleted file mode 100644 index 2edf592f68..0000000000 --- a/sandbox/linux/bpf_dsl/dump_bpf.cc +++ /dev/null @@ -1,159 +0,0 @@ -// 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/dump_bpf.h" - -#include <inttypes.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> - -#include <string> - -#include "base/strings/stringprintf.h" -#include "sandbox/linux/bpf_dsl/codegen.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 { - -const char* AluOpToken(uint32_t code) { - switch (BPF_OP(code)) { - case BPF_ADD: - return "+"; - case BPF_SUB: - return "-"; - case BPF_MUL: - return "*"; - case BPF_DIV: - return "/"; - case BPF_MOD: - return "%"; - case BPF_OR: - return "|"; - case BPF_XOR: - return "^"; - case BPF_AND: - return "&"; - case BPF_LSH: - return "<<"; - case BPF_RSH: - return ">>"; - default: - return "???"; - } -} - -const char* JmpOpToken(uint32_t code) { - switch (BPF_OP(code)) { - case BPF_JSET: - return "&"; - case BPF_JEQ: - return "=="; - case BPF_JGE: - return ">="; - default: - return "???"; - } -} - -const char* DataOffsetName(size_t off) { - switch (off) { - case SECCOMP_NR_IDX: - return "System call number"; - case SECCOMP_ARCH_IDX: - return "Architecture"; - case SECCOMP_IP_LSB_IDX: - return "Instruction pointer (LSB)"; - case SECCOMP_IP_MSB_IDX: - return "Instruction pointer (MSB)"; - default: - return "???"; - } -} - -void AppendInstruction(std::string* dst, size_t pc, const sock_filter& insn) { - base::StringAppendF(dst, "%3zu) ", pc); - switch (BPF_CLASS(insn.code)) { - case BPF_LD: - if (insn.code == BPF_LD + BPF_W + BPF_ABS) { - base::StringAppendF(dst, "LOAD %" PRIu32 " // ", insn.k); - size_t maybe_argno = - (insn.k - offsetof(struct arch_seccomp_data, args)) / - sizeof(uint64_t); - if (maybe_argno < 6 && insn.k == SECCOMP_ARG_LSB_IDX(maybe_argno)) { - base::StringAppendF(dst, "Argument %zu (LSB)\n", maybe_argno); - } else if (maybe_argno < 6 && - insn.k == SECCOMP_ARG_MSB_IDX(maybe_argno)) { - base::StringAppendF(dst, "Argument %zu (MSB)\n", maybe_argno); - } else { - base::StringAppendF(dst, "%s\n", DataOffsetName(insn.k)); - } - } else { - base::StringAppendF(dst, "Load ???\n"); - } - break; - case BPF_JMP: - if (BPF_OP(insn.code) == BPF_JA) { - base::StringAppendF(dst, "JMP %zu\n", pc + insn.k + 1); - } else { - base::StringAppendF( - dst, "if A %s 0x%" PRIx32 "; then JMP %zu else JMP %zu\n", - JmpOpToken(insn.code), insn.k, pc + insn.jt + 1, pc + insn.jf + 1); - } - break; - case BPF_RET: - base::StringAppendF(dst, "RET 0x%" PRIx32 " // ", insn.k); - if ((insn.k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP) { - base::StringAppendF(dst, "Trap #%" PRIu32 "\n", - insn.k & SECCOMP_RET_DATA); - } else if ((insn.k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { - base::StringAppendF(dst, "errno = %" PRIu32 "\n", - insn.k & SECCOMP_RET_DATA); - } else if ((insn.k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRACE) { - base::StringAppendF(dst, "Trace #%" PRIu32 "\n", - insn.k & SECCOMP_RET_DATA); - } else if (insn.k == SECCOMP_RET_ALLOW) { - base::StringAppendF(dst, "Allowed\n"); - } else if (insn.k == SECCOMP_RET_KILL) { - base::StringAppendF(dst, "Kill\n"); - } else { - base::StringAppendF(dst, "???\n"); - } - break; - case BPF_ALU: - if (BPF_OP(insn.code) == BPF_NEG) { - base::StringAppendF(dst, "A := -A\n"); - } else { - base::StringAppendF(dst, "A := A %s 0x%" PRIx32 "\n", - AluOpToken(insn.code), insn.k); - } - break; - default: - base::StringAppendF(dst, "???\n"); - break; - } -} - -} // namespace - -void DumpBPF::PrintProgram(const CodeGen::Program& program) { - fputs(StringPrintProgram(program).c_str(), stderr); -} - -std::string DumpBPF::StringPrintProgram(const CodeGen::Program& program) { - std::string res; - for (size_t i = 0; i < program.size(); i++) { - AppendInstruction(&res, i + 1, program[i]); - } - return res; -} - -} // namespace bpf_dsl -} // namespace sandbox diff --git a/sandbox/linux/bpf_dsl/dump_bpf.h b/sandbox/linux/bpf_dsl/dump_bpf.h deleted file mode 100644 index a7db58981b..0000000000 --- a/sandbox/linux/bpf_dsl/dump_bpf.h +++ /dev/null @@ -1,24 +0,0 @@ -// 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 <string> - -#include "sandbox/linux/bpf_dsl/codegen.h" -#include "sandbox/sandbox_export.h" - -namespace sandbox { -namespace bpf_dsl { - -class SANDBOX_EXPORT DumpBPF { - public: - // PrintProgram writes |program| in a human-readable format to stderr. - static void PrintProgram(const CodeGen::Program& program); - - // StringPrintProgram writes |program| in a human-readable format to - // a std::string. - static std::string StringPrintProgram(const CodeGen::Program& program); -}; - -} // namespace bpf_dsl -} // namespace sandbox diff --git a/sandbox/linux/bpf_dsl/errorcode.h b/sandbox/linux/bpf_dsl/errorcode.h deleted file mode 100644 index 611c27dd80..0000000000 --- a/sandbox/linux/bpf_dsl/errorcode.h +++ /dev/null @@ -1,37 +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. - -#ifndef SANDBOX_LINUX_BPF_DSL_ERRORCODE_H__ -#define SANDBOX_LINUX_BPF_DSL_ERRORCODE_H__ - -#include "base/macros.h" -#include "sandbox/sandbox_export.h" - -namespace sandbox { -namespace bpf_dsl { - -// TODO(mdempsky): Find a proper home for ERR_{MIN,MAX}_ERRNO and -// remove this header. -class SANDBOX_EXPORT ErrorCode { - public: - enum { - ERR_MIN_ERRNO = 0, -#if defined(__mips__) - // MIPS only supports errno up to 1133 - ERR_MAX_ERRNO = 1133, -#else - // TODO(markus): Android only supports errno up to 255 - // (crbug.com/181647). - ERR_MAX_ERRNO = 4095, -#endif - }; - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(ErrorCode); -}; - -} // namespace bpf_dsl -} // namespace sandbox - -#endif // SANDBOX_LINUX_BPF_DSL_ERRORCODE_H__ diff --git a/sandbox/linux/bpf_dsl/linux_syscall_ranges.h b/sandbox/linux/bpf_dsl/linux_syscall_ranges.h deleted file mode 100644 index a747770c78..0000000000 --- a/sandbox/linux/bpf_dsl/linux_syscall_ranges.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2015 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. - -#ifndef SANDBOX_LINUX_BPF_DSL_LINUX_SYSCALL_RANGES_H_ -#define SANDBOX_LINUX_BPF_DSL_LINUX_SYSCALL_RANGES_H_ - -#if defined(__x86_64__) - -#define MIN_SYSCALL 0u -#define MAX_PUBLIC_SYSCALL 1024u -#define MAX_SYSCALL MAX_PUBLIC_SYSCALL - -#elif defined(__i386__) - -#define MIN_SYSCALL 0u -#define MAX_PUBLIC_SYSCALL 1024u -#define MAX_SYSCALL MAX_PUBLIC_SYSCALL - -#elif defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__)) - -// ARM EABI includes "ARM private" system calls starting at |__ARM_NR_BASE|, -// and a "ghost syscall private to the kernel", cmpxchg, -// at |__ARM_NR_BASE+0x00fff0|. -// See </arch/arm/include/asm/unistd.h> in the Linux kernel. - -// __NR_SYSCALL_BASE is 0 in thumb and ARM EABI. -#define MIN_SYSCALL 0u -#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + 1024u) -// __ARM_NR_BASE is __NR_SYSCALL_BASE + 0xf0000u -#define MIN_PRIVATE_SYSCALL 0xf0000u -#define MAX_PRIVATE_SYSCALL (MIN_PRIVATE_SYSCALL + 16u) -#define MIN_GHOST_SYSCALL (MIN_PRIVATE_SYSCALL + 0xfff0u) -#define MAX_SYSCALL (MIN_GHOST_SYSCALL + 4u) - -#elif defined(__mips__) && (_MIPS_SIM == _ABIO32) - -#include <asm/unistd.h> // for __NR_O32_Linux and __NR_Linux_syscalls -#define MIN_SYSCALL __NR_O32_Linux -#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + __NR_Linux_syscalls) -#define MAX_SYSCALL MAX_PUBLIC_SYSCALL - -#elif defined(__mips__) && (_MIPS_SIM == _ABI64) - -#error "Add support to header file" - -#elif defined(__aarch64__) - -#define MIN_SYSCALL 0u -#define MAX_PUBLIC_SYSCALL 279u -#define MAX_SYSCALL MAX_PUBLIC_SYSCALL - -#else -#error "Unsupported architecture" -#endif - -#endif // SANDBOX_LINUX_BPF_DSL_LINUX_SYSCALL_RANGES_H_ diff --git a/sandbox/linux/bpf_dsl/policy.cc b/sandbox/linux/bpf_dsl/policy.cc deleted file mode 100644 index c20edc6da8..0000000000 --- a/sandbox/linux/bpf_dsl/policy.cc +++ /dev/null @@ -1,19 +0,0 @@ -// 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/policy.h" - -#include <errno.h> - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" - -namespace sandbox { -namespace bpf_dsl { - -ResultExpr Policy::InvalidSyscall() const { - return Error(ENOSYS); -} - -} // namespace bpf_dsl -} // namespace sandbox diff --git a/sandbox/linux/bpf_dsl/policy.h b/sandbox/linux/bpf_dsl/policy.h deleted file mode 100644 index 6c67589456..0000000000 --- a/sandbox/linux/bpf_dsl/policy.h +++ /dev/null @@ -1,37 +0,0 @@ -// 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. - -#ifndef SANDBOX_LINUX_BPF_DSL_POLICY_H_ -#define SANDBOX_LINUX_BPF_DSL_POLICY_H_ - -#include "base/macros.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h" -#include "sandbox/sandbox_export.h" - -namespace sandbox { -namespace bpf_dsl { - -// Interface to implement to define a BPF sandbox policy. -class SANDBOX_EXPORT Policy { - public: - Policy() {} - virtual ~Policy() {} - - // User extension point for writing custom sandbox policies. - // The returned ResultExpr will control how the kernel responds to the - // specified system call number. - virtual ResultExpr EvaluateSyscall(int sysno) const = 0; - - // Optional overload for specifying alternate behavior for invalid - // system calls. The default is to return ENOSYS. - virtual ResultExpr InvalidSyscall() const; - - private: - DISALLOW_COPY_AND_ASSIGN(Policy); -}; - -} // namespace bpf_dsl -} // namespace sandbox - -#endif // SANDBOX_LINUX_BPF_DSL_POLICY_H_ diff --git a/sandbox/linux/bpf_dsl/policy_compiler.cc b/sandbox/linux/bpf_dsl/policy_compiler.cc deleted file mode 100644 index 7ce517a5d5..0000000000 --- a/sandbox/linux/bpf_dsl/policy_compiler.cc +++ /dev/null @@ -1,466 +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/bpf_dsl/policy_compiler.h" - -#include <errno.h> -#include <stddef.h> -#include <stdint.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/bpf_dsl/codegen.h" -#include "sandbox/linux/bpf_dsl/policy.h" -#include "sandbox/linux/bpf_dsl/seccomp_macros.h" -#include "sandbox/linux/bpf_dsl/syscall_set.h" -#include "sandbox/linux/system_headers/linux_filter.h" -#include "sandbox/linux/system_headers/linux_seccomp.h" -#include "sandbox/linux/system_headers/linux_syscalls.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; -} - -ResultExpr DefaultPanic(const char* error) { - return Kill(); -} - -// 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; -} - -bool HasUnsafeTraps(const Policy* policy) { - DCHECK(policy); - for (uint32_t sysnum : SyscallSet::ValidOnly()) { - if (policy->EvaluateSyscall(sysnum)->HasUnsafeTraps()) { - return true; - } - } - return policy->InvalidSyscall()->HasUnsafeTraps(); -} - -} // namespace - -struct PolicyCompiler::Range { - uint32_t from; - CodeGen::Node node; -}; - -PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry) - : policy_(policy), - registry_(registry), - escapepc_(0), - panic_func_(DefaultPanic), - gen_(), - has_unsafe_traps_(HasUnsafeTraps(policy_)) { - DCHECK(policy); -} - -PolicyCompiler::~PolicyCompiler() { -} - -CodeGen::Program PolicyCompiler::Compile() { - CHECK(policy_->InvalidSyscall()->IsDeny()) - << "Policies should deny invalid system calls"; - - // If our BPF program has unsafe traps, enable support for them. - if (has_unsafe_traps_) { - CHECK_NE(0U, escapepc_) << "UnsafeTrap() requires a valid escape PC"; - - for (int sysnum : kSyscallsRequiredForUnsafeTraps) { - CHECK(policy_->EvaluateSyscall(sysnum)->IsAllow()) - << "Policies that use UnsafeTrap() must unconditionally allow all " - "required system calls"; - } - - CHECK(registry_->EnableUnsafeTraps()) - << "We'd rather die than enable unsafe traps"; - } - - // Assemble the BPF filter program. - return gen_.Compile(AssemblePolicy()); -} - -void PolicyCompiler::DangerousSetEscapePC(uint64_t escapepc) { - escapepc_ = escapepc; -} - -void PolicyCompiler::SetPanicFunc(PanicFunc panic_func) { - panic_func_ = panic_func; -} - -CodeGen::Node 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())); -} - -CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node 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, - CompileResult(panic_func_( - "Invalid audit architecture in BPF filter")))); -} - -CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) { - // If no unsafe traps, then simply return |rest|. - if (!has_unsafe_traps_) { - return rest; - } - - // We already enabled unsafe traps in Compile, but enable them again to give - // the trap registry a second chance to complain before we add the backdoor. - CHECK(registry_->EnableUnsafeTraps()); - - // Allow system calls, if they originate from our magic return address. - const uint32_t lopc = static_cast<uint32_t>(escapepc_); - const uint32_t hipc = static_cast<uint32_t>(escapepc_ >> 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, lopc, - gen_.MakeInstruction( - BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_MSB_IDX, - gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, hipc, - CompileResult(Allow()), rest)), - rest)); -} - -CodeGen::Node PolicyCompiler::DispatchSyscall() { - // Evaluate all possible system calls and group their Nodes into - // ranges of identical codes. - Ranges ranges; - FindRanges(&ranges); - - // Compile the system call ranges to an optimized BPF jumptable - CodeGen::Node 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)); -} - -CodeGen::Node PolicyCompiler::CheckSyscallNumber(CodeGen::Node passed) { - if (kIsIntel) { - // On Intel architectures, verify that system call numbers are in the - // expected number range. - CodeGen::Node invalidX32 = - CompileResult(panic_func_("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 Node. - const CodeGen::Node invalid_node = CompileResult(policy_->InvalidSyscall()); - uint32_t old_sysnum = 0; - CodeGen::Node old_node = - SyscallSet::IsValid(old_sysnum) - ? CompileResult(policy_->EvaluateSyscall(old_sysnum)) - : invalid_node; - - for (uint32_t sysnum : SyscallSet::All()) { - CodeGen::Node node = - SyscallSet::IsValid(sysnum) - ? CompileResult(policy_->EvaluateSyscall(static_cast<int>(sysnum))) - : invalid_node; - // N.B., here we rely on CodeGen folding (i.e., returning the same - // node value for) identical code sequences, otherwise our jump - // table will blow up in size. - if (node != old_node) { - ranges->push_back(Range{old_sysnum, old_node}); - old_sysnum = sysnum; - old_node = node; - } - } - ranges->push_back(Range{old_sysnum, old_node}); -} - -CodeGen::Node 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. - CHECK(start < stop) << "Invalid iterator range"; - const auto n = stop - start; - if (n == 1) { - // If we have narrowed things down to a single range object, we can - // return from the BPF filter program. - return start->node; - } - - // 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 + n / 2; - - // Sub-divide the list of ranges and continue recursively. - CodeGen::Node jf = AssembleJumpTable(start, mid); - CodeGen::Node jt = AssembleJumpTable(mid, stop); - return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf); -} - -CodeGen::Node PolicyCompiler::CompileResult(const ResultExpr& res) { - return res->Compile(this); -} - -CodeGen::Node PolicyCompiler::MaskedEqual(int argno, - size_t width, - uint64_t mask, - uint64_t value, - CodeGen::Node passed, - CodeGen::Node failed) { - // Sanity check that arguments make sense. - CHECK(argno >= 0 && argno < 6) << "Invalid argument number " << argno; - CHECK(width == 4 || width == 8) << "Invalid argument width " << width; - CHECK_NE(0U, mask) << "Zero mask is invalid"; - CHECK_EQ(value, value & mask) << "Value contains masked out bits"; - if (sizeof(void*) == 4) { - CHECK_EQ(4U, width) << "Invalid width on 32-bit platform"; - } - if (width == 4) { - CHECK_EQ(0U, mask >> 32) << "Mask exceeds argument size"; - CHECK_EQ(0U, value >> 32) << "Value exceeds argument size"; - } - - // 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 MaskedEqualHalf(argno, width, mask, value, ArgHalf::UPPER, - MaskedEqualHalf(argno, width, mask, value, - ArgHalf::LOWER, passed, failed), - failed); -} - -CodeGen::Node PolicyCompiler::MaskedEqualHalf(int argno, - size_t width, - uint64_t full_mask, - uint64_t full_value, - ArgHalf half, - CodeGen::Node passed, - CodeGen::Node failed) { - if (width == 4 && half == ArgHalf::UPPER) { - // Special logic for sanity checking the upper 32-bits of 32-bit system - // call arguments. - - // TODO(mdempsky): Compile Unexpected64bitArgument() just per program. - CodeGen::Node invalid_64bit = Unexpected64bitArgument(); - - const uint32_t upper = SECCOMP_ARG_MSB_IDX(argno); - const uint32_t lower = SECCOMP_ARG_LSB_IDX(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 == ArgHalf::UPPER) ? SECCOMP_ARG_MSB_IDX(argno) - : SECCOMP_ARG_LSB_IDX(argno); - const uint32_t mask = (half == ArgHalf::UPPER) ? full_mask >> 32 : full_mask; - const uint32_t value = - (half == ArgHalf::UPPER) ? full_value >> 32 : full_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))); -} - -CodeGen::Node PolicyCompiler::Unexpected64bitArgument() { - return CompileResult(panic_func_("Unexpected 64bit argument detected")); -} - -CodeGen::Node PolicyCompiler::Return(uint32_t ret) { - if (has_unsafe_traps_ && (ret & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { - // 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*>(ret & SECCOMP_RET_DATA), - true); - } - - return gen_.MakeInstruction(BPF_RET + BPF_K, ret); -} - -CodeGen::Node PolicyCompiler::Trap(TrapRegistry::TrapFnc fnc, - const void* aux, - bool safe) { - uint16_t trap_id = registry_->Add(fnc, aux, safe); - return gen_.MakeInstruction(BPF_RET + BPF_K, SECCOMP_RET_TRAP + trap_id); -} - -bool PolicyCompiler::IsRequiredForUnsafeTrap(int sysno) { - for (size_t i = 0; i < arraysize(kSyscallsRequiredForUnsafeTraps); ++i) { - if (sysno == kSyscallsRequiredForUnsafeTraps[i]) { - return true; - } - } - return false; -} - -} // namespace bpf_dsl -} // namespace sandbox diff --git a/sandbox/linux/bpf_dsl/policy_compiler.h b/sandbox/linux/bpf_dsl/policy_compiler.h deleted file mode 100644 index 48b1d780d9..0000000000 --- a/sandbox/linux/bpf_dsl/policy_compiler.h +++ /dev/null @@ -1,153 +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. - -#ifndef SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_ -#define SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <vector> - -#include "base/macros.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h" -#include "sandbox/linux/bpf_dsl/codegen.h" -#include "sandbox/linux/bpf_dsl/trap_registry.h" -#include "sandbox/sandbox_export.h" - -namespace sandbox { -namespace bpf_dsl { -class Policy; - -// PolicyCompiler implements the bpf_dsl compiler, allowing users to -// transform bpf_dsl policies into BPF programs to be executed by the -// Linux kernel. -class SANDBOX_EXPORT PolicyCompiler { - public: - using PanicFunc = bpf_dsl::ResultExpr (*)(const char* error); - - PolicyCompiler(const Policy* policy, TrapRegistry* registry); - ~PolicyCompiler(); - - // Compile registers any trap handlers needed by the policy and - // compiles the policy to a BPF program, which it returns. - CodeGen::Program Compile(); - - // DangerousSetEscapePC sets the "escape PC" that is allowed to issue any - // system calls, regardless of policy. - void DangerousSetEscapePC(uint64_t escapepc); - - // SetPanicFunc sets the callback function used for handling faulty - // system call conditions. The default behavior is to immediately kill - // the process. - // TODO(mdempsky): Move this into Policy? - void SetPanicFunc(PanicFunc panic_func); - - // UnsafeTraps require some syscalls to always be allowed. - // This helper function returns true for these calls. - static bool IsRequiredForUnsafeTrap(int sysno); - - // Functions below are meant for use within bpf_dsl itself. - - // Return returns a CodeGen::Node that returns the specified seccomp - // return value. - CodeGen::Node Return(uint32_t ret); - - // Trap returns a CodeGen::Node to indicate the system call should - // instead invoke a trap handler. - CodeGen::Node Trap(TrapRegistry::TrapFnc fnc, const void* aux, bool safe); - - // MaskedEqual returns a CodeGen::Node that represents a conditional branch. - // Argument "argno" (1..6) will be bitwise-AND'd with "mask" and compared - // to "value"; if equal, then "passed" will be executed, otherwise "failed". - // If "width" is 4, the argument must in the range of 0x0..(1u << 32 - 1) - // If it is outside this range, the sandbox treats the system call just - // the same as any other ABI violation (i.e., it panics). - CodeGen::Node MaskedEqual(int argno, - size_t width, - uint64_t mask, - uint64_t value, - CodeGen::Node passed, - CodeGen::Node failed); - - private: - struct Range; - typedef std::vector<Range> Ranges; - - // Used by MaskedEqualHalf to track which half of the argument it's - // emitting instructions for. - enum class ArgHalf { - LOWER, - UPPER, - }; - - // Compile the configured policy into a complete instruction sequence. - CodeGen::Node AssemblePolicy(); - - // Return an instruction sequence that checks the - // arch_seccomp_data's "arch" field is valid, and then passes - // control to |passed| if so. - CodeGen::Node CheckArch(CodeGen::Node passed); - - // If |has_unsafe_traps_| is true, returns an instruction sequence - // that allows all system calls from |escapepc_|, and otherwise - // passes control to |rest|. Otherwise, simply returns |rest|. - CodeGen::Node MaybeAddEscapeHatch(CodeGen::Node rest); - - // Return an instruction sequence that loads and checks the system - // call number, performs a binary search, and then dispatches to an - // appropriate instruction sequence compiled from the current - // policy. - CodeGen::Node DispatchSyscall(); - - // Return an instruction sequence that checks the system call number - // (expected to be loaded in register A) and if valid, passes - // control to |passed| (with register A still valid). - CodeGen::Node CheckSyscallNumber(CodeGen::Node passed); - - // Finds all the ranges of system calls that need to be handled. Ranges are - // sorted in ascending order of system call numbers. There are no gaps in the - // ranges. System calls with identical CodeGen::Nodes are coalesced into a - // single - // range. - void FindRanges(Ranges* ranges); - - // Returns a BPF program snippet that implements a jump table for the - // given range of system call numbers. This function runs recursively. - CodeGen::Node AssembleJumpTable(Ranges::const_iterator start, - Ranges::const_iterator stop); - - // CompileResult compiles an individual result expression into a - // CodeGen node. - CodeGen::Node CompileResult(const ResultExpr& res); - - // Returns a BPF program that evaluates half of a conditional expression; - // it should only ever be called from CondExpression(). - CodeGen::Node MaskedEqualHalf(int argno, - size_t width, - uint64_t full_mask, - uint64_t full_value, - ArgHalf half, - CodeGen::Node passed, - CodeGen::Node failed); - - // Returns the fatal CodeGen::Node that is used to indicate that somebody - // attempted to pass a 64bit value in a 32bit system call argument. - CodeGen::Node Unexpected64bitArgument(); - - const Policy* policy_; - TrapRegistry* registry_; - uint64_t escapepc_; - PanicFunc panic_func_; - - CodeGen gen_; - bool has_unsafe_traps_; - - DISALLOW_COPY_AND_ASSIGN(PolicyCompiler); -}; - -} // namespace bpf_dsl -} // namespace sandbox - -#endif // SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_ diff --git a/sandbox/linux/bpf_dsl/seccomp_macros.h b/sandbox/linux/bpf_dsl/seccomp_macros.h deleted file mode 100644 index af70f21cd7..0000000000 --- a/sandbox/linux/bpf_dsl/seccomp_macros.h +++ /dev/null @@ -1,294 +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. - -#ifndef SANDBOX_LINUX_BPF_DSL_SECCOMP_MACROS_H_ -#define SANDBOX_LINUX_BPF_DSL_SECCOMP_MACROS_H_ - -#include <sys/types.h> // For __BIONIC__. -// Old Bionic versions do not have sys/user.h. The if can be removed once we no -// longer need to support these old Bionic versions. -// All x86_64 builds use a new enough bionic to have sys/user.h. -#if !defined(__BIONIC__) || defined(__x86_64__) -#if !defined(__native_client_nonsfi__) -#include <sys/user.h> -#endif -#if defined(__mips__) -// sys/user.h in eglibc misses size_t definition -#include <stddef.h> -#endif -#endif - -#include "sandbox/linux/system_headers/linux_seccomp.h" // For AUDIT_ARCH_* - -// Impose some reasonable maximum BPF program size. Realistically, the -// kernel probably has much lower limits. But by limiting to less than -// 30 bits, we can ease requirements on some of our data types. -#define SECCOMP_MAX_PROGRAM_SIZE (1<<30) - -#if defined(__i386__) -#define SECCOMP_ARCH AUDIT_ARCH_I386 - -#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)]) -#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_EAX) -#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_EAX) -#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_EIP) -#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_EBX) -#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_ECX) -#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_EDX) -#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_ESI) -#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_EDI) -#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_EBP) -#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr)) -#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch)) -#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \ - instruction_pointer) + 4) -#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \ - instruction_pointer) + 0) -#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \ - 8*(nr) + 4) -#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \ - 8*(nr) + 0) - - -#if defined(__BIONIC__) || defined(__native_client_nonsfi__) -// Old Bionic versions and PNaCl toolchain don't have sys/user.h, so we just -// define regs_struct directly. This can be removed once we no longer need to -// support these old Bionic versions and PNaCl toolchain. -struct regs_struct { - long int ebx; - long int ecx; - long int edx; - long int esi; - long int edi; - long int ebp; - long int eax; - long int xds; - long int xes; - long int xfs; - long int xgs; - long int orig_eax; - long int eip; - long int xcs; - long int eflags; - long int esp; - long int xss; -}; -#else -typedef user_regs_struct regs_struct; -#endif - -#define SECCOMP_PT_RESULT(_regs) (_regs).eax -#define SECCOMP_PT_SYSCALL(_regs) (_regs).orig_eax -#define SECCOMP_PT_IP(_regs) (_regs).eip -#define SECCOMP_PT_PARM1(_regs) (_regs).ebx -#define SECCOMP_PT_PARM2(_regs) (_regs).ecx -#define SECCOMP_PT_PARM3(_regs) (_regs).edx -#define SECCOMP_PT_PARM4(_regs) (_regs).esi -#define SECCOMP_PT_PARM5(_regs) (_regs).edi -#define SECCOMP_PT_PARM6(_regs) (_regs).ebp - -#elif defined(__x86_64__) -#define SECCOMP_ARCH AUDIT_ARCH_X86_64 - -#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)]) -#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_RAX) -#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_RAX) -#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_RIP) -#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_RDI) -#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_RSI) -#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_RDX) -#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_R10) -#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_R8) -#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_R9) -#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr)) -#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch)) -#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \ - instruction_pointer) + 4) -#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \ - instruction_pointer) + 0) -#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \ - 8*(nr) + 4) -#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \ - 8*(nr) + 0) - -typedef user_regs_struct regs_struct; -#define SECCOMP_PT_RESULT(_regs) (_regs).rax -#define SECCOMP_PT_SYSCALL(_regs) (_regs).orig_rax -#define SECCOMP_PT_IP(_regs) (_regs).rip -#define SECCOMP_PT_PARM1(_regs) (_regs).rdi -#define SECCOMP_PT_PARM2(_regs) (_regs).rsi -#define SECCOMP_PT_PARM3(_regs) (_regs).rdx -#define SECCOMP_PT_PARM4(_regs) (_regs).r10 -#define SECCOMP_PT_PARM5(_regs) (_regs).r8 -#define SECCOMP_PT_PARM6(_regs) (_regs).r9 - -#elif defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__)) -#define SECCOMP_ARCH AUDIT_ARCH_ARM - -// ARM sigcontext_t is different from i386/x86_64. -// See </arch/arm/include/asm/sigcontext.h> in the Linux kernel. -#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.arm_##_reg) -// ARM EABI syscall convention. -#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, r0) -#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, r7) -#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, pc) -#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, r0) -#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, r1) -#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, r2) -#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, r3) -#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, r4) -#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, r5) -#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr)) -#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch)) -#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \ - instruction_pointer) + 4) -#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \ - instruction_pointer) + 0) -#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \ - 8*(nr) + 4) -#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \ - 8*(nr) + 0) - -#if defined(__BIONIC__) || defined(__native_client_nonsfi__) -// Old Bionic versions and PNaCl toolchain don't have sys/user.h, so we just -// define regs_struct directly. This can be removed once we no longer need to -// support these old Bionic versions and PNaCl toolchain. -struct regs_struct { - unsigned long uregs[18]; -}; -#else -typedef user_regs regs_struct; -#endif - -#define REG_cpsr uregs[16] -#define REG_pc uregs[15] -#define REG_lr uregs[14] -#define REG_sp uregs[13] -#define REG_ip uregs[12] -#define REG_fp uregs[11] -#define REG_r10 uregs[10] -#define REG_r9 uregs[9] -#define REG_r8 uregs[8] -#define REG_r7 uregs[7] -#define REG_r6 uregs[6] -#define REG_r5 uregs[5] -#define REG_r4 uregs[4] -#define REG_r3 uregs[3] -#define REG_r2 uregs[2] -#define REG_r1 uregs[1] -#define REG_r0 uregs[0] -#define REG_ORIG_r0 uregs[17] - -#define SECCOMP_PT_RESULT(_regs) (_regs).REG_r0 -#define SECCOMP_PT_SYSCALL(_regs) (_regs).REG_r7 -#define SECCOMP_PT_IP(_regs) (_regs).REG_pc -#define SECCOMP_PT_PARM1(_regs) (_regs).REG_r0 -#define SECCOMP_PT_PARM2(_regs) (_regs).REG_r1 -#define SECCOMP_PT_PARM3(_regs) (_regs).REG_r2 -#define SECCOMP_PT_PARM4(_regs) (_regs).REG_r3 -#define SECCOMP_PT_PARM5(_regs) (_regs).REG_r4 -#define SECCOMP_PT_PARM6(_regs) (_regs).REG_r5 - -#elif defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32) -#define SECCOMP_ARCH AUDIT_ARCH_MIPSEL -#define SYSCALL_EIGHT_ARGS -// MIPS sigcontext_t is different from i386/x86_64 and ARM. -// See </arch/mips/include/uapi/asm/sigcontext.h> in the Linux kernel. -#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[_reg]) -// Based on MIPS o32 ABI syscall convention. -// On MIPS, when indirect syscall is being made (syscall(__NR_foo)), -// real identificator (__NR_foo) is not in v0, but in a0 -#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, 2) -#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, 2) -#define SECCOMP_IP(_ctx) (_ctx)->uc_mcontext.pc -#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, 4) -#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, 5) -#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, 6) -#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, 7) -// Only the first 4 arguments of syscall are in registers. -// The rest are on the stack. -#define SECCOMP_STACKPARM(_ctx, n) (((long *)SECCOMP_REG(_ctx, 29))[(n)]) -#define SECCOMP_PARM5(_ctx) SECCOMP_STACKPARM(_ctx, 4) -#define SECCOMP_PARM6(_ctx) SECCOMP_STACKPARM(_ctx, 5) -#define SECCOMP_PARM7(_ctx) SECCOMP_STACKPARM(_ctx, 6) -#define SECCOMP_PARM8(_ctx) SECCOMP_STACKPARM(_ctx, 7) -#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr)) -#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch)) -#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \ - instruction_pointer) + 4) -#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \ - instruction_pointer) + 0) -#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \ - 8*(nr) + 4) -#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \ - 8*(nr) + 0) - -// On Mips we don't have structures like user_regs or user_regs_struct in -// sys/user.h that we could use, so we just define regs_struct directly. -struct regs_struct { - unsigned long long regs[32]; -}; - -#define REG_a3 regs[7] -#define REG_a2 regs[6] -#define REG_a1 regs[5] -#define REG_a0 regs[4] -#define REG_v1 regs[3] -#define REG_v0 regs[2] - -#define SECCOMP_PT_RESULT(_regs) (_regs).REG_v0 -#define SECCOMP_PT_SYSCALL(_regs) (_regs).REG_v0 -#define SECCOMP_PT_PARM1(_regs) (_regs).REG_a0 -#define SECCOMP_PT_PARM2(_regs) (_regs).REG_a1 -#define SECCOMP_PT_PARM3(_regs) (_regs).REG_a2 -#define SECCOMP_PT_PARM4(_regs) (_regs).REG_a3 - -#elif defined(__aarch64__) -struct regs_struct { - unsigned long long regs[31]; - unsigned long long sp; - unsigned long long pc; - unsigned long long pstate; -}; - -#define SECCOMP_ARCH AUDIT_ARCH_AARCH64 - -#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.regs[_reg]) - -#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, 0) -#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, 8) -#define SECCOMP_IP(_ctx) (_ctx)->uc_mcontext.pc -#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, 0) -#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, 1) -#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, 2) -#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, 3) -#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, 4) -#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, 5) - -#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr)) -#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch)) -#define SECCOMP_IP_MSB_IDX \ - (offsetof(struct arch_seccomp_data, instruction_pointer) + 4) -#define SECCOMP_IP_LSB_IDX \ - (offsetof(struct arch_seccomp_data, instruction_pointer) + 0) -#define SECCOMP_ARG_MSB_IDX(nr) \ - (offsetof(struct arch_seccomp_data, args) + 8 * (nr) + 4) -#define SECCOMP_ARG_LSB_IDX(nr) \ - (offsetof(struct arch_seccomp_data, args) + 8 * (nr) + 0) - -#define SECCOMP_PT_RESULT(_regs) (_regs).regs[0] -#define SECCOMP_PT_SYSCALL(_regs) (_regs).regs[8] -#define SECCOMP_PT_IP(_regs) (_regs).pc -#define SECCOMP_PT_PARM1(_regs) (_regs).regs[0] -#define SECCOMP_PT_PARM2(_regs) (_regs).regs[1] -#define SECCOMP_PT_PARM3(_regs) (_regs).regs[2] -#define SECCOMP_PT_PARM4(_regs) (_regs).regs[3] -#define SECCOMP_PT_PARM5(_regs) (_regs).regs[4] -#define SECCOMP_PT_PARM6(_regs) (_regs).regs[5] -#else -#error Unsupported target platform - -#endif - -#endif // SANDBOX_LINUX_BPF_DSL_SECCOMP_MACROS_H_ diff --git a/sandbox/linux/bpf_dsl/syscall_set.cc b/sandbox/linux/bpf_dsl/syscall_set.cc deleted file mode 100644 index 3d61fa31fd..0000000000 --- a/sandbox/linux/bpf_dsl/syscall_set.cc +++ /dev/null @@ -1,146 +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/bpf_dsl/syscall_set.h" - -#include <stdint.h> - -#include "base/logging.h" -#include "base/macros.h" -#include "sandbox/linux/bpf_dsl/linux_syscall_ranges.h" - -namespace sandbox { - -namespace { - -#if defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32) -// This is true for Mips O32 ABI. -static_assert(MIN_SYSCALL == __NR_Linux, "min syscall number should be 4000"); -#else -// This true for supported architectures (Intel and ARM EABI). -static_assert(MIN_SYSCALL == 0u, - "min syscall should always be zero"); -#endif - -// SyscallRange represents an inclusive range of system call numbers. -struct SyscallRange { - uint32_t first; - uint32_t last; -}; - -const SyscallRange kValidSyscallRanges[] = { - // First we iterate up to MAX_PUBLIC_SYSCALL, which is equal to MAX_SYSCALL - // on Intel architectures, but leaves room for private syscalls on ARM. - {MIN_SYSCALL, MAX_PUBLIC_SYSCALL}, -#if defined(__arm__) - // ARM EABI includes "ARM private" system calls starting at - // MIN_PRIVATE_SYSCALL, and a "ghost syscall private to the kernel" at - // MIN_GHOST_SYSCALL. - {MIN_PRIVATE_SYSCALL, MAX_PRIVATE_SYSCALL}, - {MIN_GHOST_SYSCALL, MAX_SYSCALL}, -#endif -}; - -} // namespace - -SyscallSet::Iterator SyscallSet::begin() const { - return Iterator(set_, false); -} - -SyscallSet::Iterator SyscallSet::end() const { - return Iterator(set_, true); -} - -bool SyscallSet::IsValid(uint32_t num) { - for (const SyscallRange& range : kValidSyscallRanges) { - if (num >= range.first && num <= range.last) { - return true; - } - } - return false; -} - -bool operator==(const SyscallSet& lhs, const SyscallSet& rhs) { - return (lhs.set_ == rhs.set_); -} - -SyscallSet::Iterator::Iterator(Set set, bool done) - : set_(set), done_(done), num_(0) { - // If the set doesn't contain 0, we need to skip to the next element. - if (!done && set_ == (IsValid(num_) ? Set::INVALID_ONLY : Set::VALID_ONLY)) { - ++*this; - } -} - -uint32_t SyscallSet::Iterator::operator*() const { - DCHECK(!done_); - return num_; -} - -SyscallSet::Iterator& SyscallSet::Iterator::operator++() { - DCHECK(!done_); - - num_ = NextSyscall(); - if (num_ == 0) { - done_ = true; - } - - return *this; -} - -// NextSyscall returns the next system call in the iterated system -// call set after |num_|, or 0 if no such system call exists. -uint32_t SyscallSet::Iterator::NextSyscall() const { - const bool want_valid = (set_ != Set::INVALID_ONLY); - const bool want_invalid = (set_ != Set::VALID_ONLY); - - for (const SyscallRange& range : kValidSyscallRanges) { - if (want_invalid && range.first > 0 && num_ < range.first - 1) { - // Even when iterating invalid syscalls, we only include the end points; - // so skip directly to just before the next (valid) range. - return range.first - 1; - } - if (want_valid && num_ < range.first) { - return range.first; - } - if (want_valid && num_ < range.last) { - return num_ + 1; - } - if (want_invalid && num_ <= range.last) { - return range.last + 1; - } - } - - if (want_invalid) { - // BPF programs only ever operate on unsigned quantities. So, - // that's how we iterate; we return values from - // 0..0xFFFFFFFFu. But there are places, where the kernel might - // interpret system call numbers as signed quantities, so the - // boundaries between signed and unsigned values are potential - // problem cases. We want to explicitly return these values from - // our iterator. - if (num_ < 0x7FFFFFFFu) - return 0x7FFFFFFFu; - if (num_ < 0x80000000u) - return 0x80000000u; - - if (num_ < 0xFFFFFFFFu) - return 0xFFFFFFFFu; - } - - return 0; -} - -bool operator==(const SyscallSet::Iterator& lhs, - const SyscallSet::Iterator& rhs) { - DCHECK(lhs.set_ == rhs.set_); - return (lhs.done_ == rhs.done_) && (lhs.num_ == rhs.num_); -} - -bool operator!=(const SyscallSet::Iterator& lhs, - const SyscallSet::Iterator& rhs) { - return !(lhs == rhs); -} - -} // namespace sandbox diff --git a/sandbox/linux/bpf_dsl/syscall_set.h b/sandbox/linux/bpf_dsl/syscall_set.h deleted file mode 100644 index b9f076d932..0000000000 --- a/sandbox/linux/bpf_dsl/syscall_set.h +++ /dev/null @@ -1,103 +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. - -#ifndef SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__ -#define SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__ - -#include <stdint.h> - -#include <iterator> - -#include "base/macros.h" -#include "sandbox/sandbox_export.h" - -namespace sandbox { - -// Iterates over the entire system call range from 0..0xFFFFFFFFu. This -// iterator is aware of how system calls look like and will skip quickly -// over ranges that can't contain system calls. It iterates more slowly -// whenever it reaches a range that is potentially problematic, returning -// the last invalid value before a valid range of system calls, and the -// first invalid value after a valid range of syscalls. It iterates over -// individual values whenever it is in the normal range for system calls -// (typically MIN_SYSCALL..MAX_SYSCALL). -// -// Example usage: -// for (uint32_t sysnum : SyscallSet::All()) { -// // Do something with sysnum. -// } -class SANDBOX_EXPORT SyscallSet { - public: - class Iterator; - - SyscallSet(const SyscallSet& ss) : set_(ss.set_) {} - ~SyscallSet() {} - - Iterator begin() const; - Iterator end() const; - - // All returns a SyscallSet that contains both valid and invalid - // system call numbers. - static SyscallSet All() { return SyscallSet(Set::ALL); } - - // ValidOnly returns a SyscallSet that contains only valid system - // call numbers. - static SyscallSet ValidOnly() { return SyscallSet(Set::VALID_ONLY); } - - // InvalidOnly returns a SyscallSet that contains only invalid - // system call numbers, but still omits numbers in the middle of a - // range of invalid system call numbers. - static SyscallSet InvalidOnly() { return SyscallSet(Set::INVALID_ONLY); } - - // IsValid returns whether |num| specifies a valid system call - // number. - static bool IsValid(uint32_t num); - - private: - enum class Set { ALL, VALID_ONLY, INVALID_ONLY }; - - explicit SyscallSet(Set set) : set_(set) {} - - Set set_; - - friend bool operator==(const SyscallSet&, const SyscallSet&); - DISALLOW_ASSIGN(SyscallSet); -}; - -SANDBOX_EXPORT bool operator==(const SyscallSet& lhs, const SyscallSet& rhs); - -// Iterator provides C++ input iterator semantics for traversing a -// SyscallSet. -class SyscallSet::Iterator - : public std::iterator<std::input_iterator_tag, uint32_t> { - public: - Iterator(const Iterator& it) - : set_(it.set_), done_(it.done_), num_(it.num_) {} - ~Iterator() {} - - uint32_t operator*() const; - Iterator& operator++(); - - private: - Iterator(Set set, bool done); - - uint32_t NextSyscall() const; - - Set set_; - bool done_; - uint32_t num_; - - friend SyscallSet; - friend bool operator==(const Iterator&, const Iterator&); - DISALLOW_ASSIGN(Iterator); -}; - -SANDBOX_EXPORT bool operator==(const SyscallSet::Iterator& lhs, - const SyscallSet::Iterator& rhs); -SANDBOX_EXPORT bool operator!=(const SyscallSet::Iterator& lhs, - const SyscallSet::Iterator& rhs); - -} // namespace sandbox - -#endif // SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__ diff --git a/sandbox/linux/bpf_dsl/syscall_set_unittest.cc b/sandbox/linux/bpf_dsl/syscall_set_unittest.cc deleted file mode 100644 index 5069e8e431..0000000000 --- a/sandbox/linux/bpf_dsl/syscall_set_unittest.cc +++ /dev/null @@ -1,126 +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/bpf_dsl/syscall_set.h" - -#include <stddef.h> -#include <stdint.h> - -#include "base/macros.h" -#include "sandbox/linux/bpf_dsl/linux_syscall_ranges.h" -#include "sandbox/linux/tests/unit_tests.h" - -namespace sandbox { - -namespace { - -const SyscallSet kSyscallSets[] = { - SyscallSet::All(), - SyscallSet::InvalidOnly(), -}; - -SANDBOX_TEST(SyscallSet, Monotonous) { - for (const SyscallSet& set : kSyscallSets) { - uint32_t prev = 0; - bool have_prev = false; - for (uint32_t sysnum : set) { - if (have_prev) { - SANDBOX_ASSERT(sysnum > prev); - } else if (set == SyscallSet::All()) { - // The iterator should start at 0. - SANDBOX_ASSERT(sysnum == 0); - } - - prev = sysnum; - have_prev = true; - } - - // The iterator should always return 0xFFFFFFFFu as the last value. - SANDBOX_ASSERT(have_prev); - SANDBOX_ASSERT(prev == 0xFFFFFFFFu); - } -} - -// AssertRange checks that SyscallIterator produces all system call -// numbers in the inclusive range [min, max]. -void AssertRange(uint32_t min, uint32_t max) { - SANDBOX_ASSERT(min < max); - uint32_t prev = min - 1; - for (uint32_t sysnum : SyscallSet::All()) { - if (sysnum >= min && sysnum <= max) { - SANDBOX_ASSERT(prev == sysnum - 1); - prev = sysnum; - } - } - SANDBOX_ASSERT(prev == max); -} - -SANDBOX_TEST(SyscallSet, ValidSyscallRanges) { - AssertRange(MIN_SYSCALL, MAX_PUBLIC_SYSCALL); -#if defined(__arm__) - AssertRange(MIN_PRIVATE_SYSCALL, MAX_PRIVATE_SYSCALL); - AssertRange(MIN_GHOST_SYSCALL, MAX_SYSCALL); -#endif -} - -SANDBOX_TEST(SyscallSet, InvalidSyscalls) { - static const uint32_t kExpected[] = { -#if defined(__mips__) - 0, - MIN_SYSCALL - 1, -#endif - MAX_PUBLIC_SYSCALL + 1, -#if defined(__arm__) - MIN_PRIVATE_SYSCALL - 1, - MAX_PRIVATE_SYSCALL + 1, - MIN_GHOST_SYSCALL - 1, - MAX_SYSCALL + 1, -#endif - 0x7FFFFFFFu, - 0x80000000u, - 0xFFFFFFFFu, - }; - - for (const SyscallSet& set : kSyscallSets) { - size_t i = 0; - for (uint32_t sysnum : set) { - if (!SyscallSet::IsValid(sysnum)) { - SANDBOX_ASSERT(i < arraysize(kExpected)); - SANDBOX_ASSERT(kExpected[i] == sysnum); - ++i; - } - } - SANDBOX_ASSERT(i == arraysize(kExpected)); - } -} - -SANDBOX_TEST(SyscallSet, ValidOnlyIsOnlyValid) { - for (uint32_t sysnum : SyscallSet::ValidOnly()) { - SANDBOX_ASSERT(SyscallSet::IsValid(sysnum)); - } -} - -SANDBOX_TEST(SyscallSet, InvalidOnlyIsOnlyInvalid) { - for (uint32_t sysnum : SyscallSet::InvalidOnly()) { - SANDBOX_ASSERT(!SyscallSet::IsValid(sysnum)); - } -} - -SANDBOX_TEST(SyscallSet, AllIsValidOnlyPlusInvalidOnly) { - std::vector<uint32_t> merged; - const SyscallSet valid_only = SyscallSet::ValidOnly(); - const SyscallSet invalid_only = SyscallSet::InvalidOnly(); - std::merge(valid_only.begin(), - valid_only.end(), - invalid_only.begin(), - invalid_only.end(), - std::back_inserter(merged)); - - const SyscallSet all = SyscallSet::All(); - SANDBOX_ASSERT(merged == std::vector<uint32_t>(all.begin(), all.end())); -} - -} // namespace - -} // namespace sandbox diff --git a/sandbox/linux/bpf_dsl/trap_registry.h b/sandbox/linux/bpf_dsl/trap_registry.h deleted file mode 100644 index 0a5d2f14cc..0000000000 --- a/sandbox/linux/bpf_dsl/trap_registry.h +++ /dev/null @@ -1,73 +0,0 @@ -// 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. - -#ifndef SANDBOX_LINUX_BPF_DSL_TRAP_REGISTRY_H_ -#define SANDBOX_LINUX_BPF_DSL_TRAP_REGISTRY_H_ - -#include <stdint.h> - -#include "base/macros.h" -#include "sandbox/sandbox_export.h" - -namespace sandbox { - -// This must match the kernel's seccomp_data structure. -struct arch_seccomp_data { - int nr; - uint32_t arch; - uint64_t instruction_pointer; - uint64_t args[6]; -}; - -namespace bpf_dsl { - -// TrapRegistry provides an interface for registering "trap handlers" -// by associating them with non-zero 16-bit trap IDs. Trap IDs should -// remain valid for the lifetime of the trap registry. -class SANDBOX_EXPORT TrapRegistry { - public: - // TrapFnc is a pointer to a function that fulfills the trap handler - // function signature. - // - // Trap handlers follow the calling convention of native system - // calls; e.g., to report an error, they return an exit code in the - // range -1..-4096 instead of directly modifying errno. However, - // modifying errno is harmless, as the original value will be - // restored afterwards. - // - // Trap handlers are executed from signal context and possibly an - // async-signal context, so they must be async-signal safe: - // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html - typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void* aux); - - // Add registers the specified trap handler tuple and returns a - // non-zero trap ID that uniquely identifies the tuple for the life - // time of the trap registry. If the same tuple is registered - // multiple times, the same value will be returned each time. - virtual uint16_t Add(TrapFnc fnc, const void* aux, bool safe) = 0; - - // EnableUnsafeTraps tries to enable unsafe traps and returns - // whether it was successful. This is a one-way operation. - // - // CAUTION: Enabling unsafe traps effectively defeats the security - // guarantees provided by the sandbox policy. TrapRegistry - // implementations should ensure unsafe traps are only enabled - // during testing. - virtual bool EnableUnsafeTraps() = 0; - - protected: - TrapRegistry() {} - - // TrapRegistry's destructor is intentionally non-virtual so that - // implementations can omit their destructor. Instead we protect against - // misuse by marking it protected. - ~TrapRegistry() {} - - DISALLOW_COPY_AND_ASSIGN(TrapRegistry); -}; - -} // namespace bpf_dsl -} // namespace sandbox - -#endif // SANDBOX_LINUX_BPF_DSL_TRAP_REGISTRY_H_ diff --git a/sandbox/linux/bpf_dsl/verifier.cc b/sandbox/linux/bpf_dsl/verifier.cc deleted file mode 100644 index b5383e5d04..0000000000 --- a/sandbox/linux/bpf_dsl/verifier.cc +++ /dev/null @@ -1,225 +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/bpf_dsl/verifier.h" - -#include <stdint.h> -#include <string.h> - -#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<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); -}; - -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 - -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_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 diff --git a/sandbox/linux/bpf_dsl/verifier.h b/sandbox/linux/bpf_dsl/verifier.h deleted file mode 100644 index 9b25ab1d71..0000000000 --- a/sandbox/linux/bpf_dsl/verifier.h +++ /dev/null @@ -1,45 +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. - -#ifndef SANDBOX_LINUX_BPF_DSL_VERIFIER_H__ -#define SANDBOX_LINUX_BPF_DSL_VERIFIER_H__ - -#include <stdint.h> - -#include <vector> - -#include "base/macros.h" -#include "sandbox/sandbox_export.h" - -struct sock_filter; - -namespace sandbox { -struct arch_seccomp_data; - -namespace bpf_dsl { - -// TODO(mdempsky): This class doesn't perform any verification any more, so it -// deserves a new name. -class SANDBOX_EXPORT Verifier { - public: - // Evaluate a given BPF program for a particular set of system call - // parameters. If evaluation failed for any reason, "err" will be set to - // a non-NULL error string. Otherwise, the BPF program's result will be - // returned by the function and "err" is NULL. - // We do not actually implement the full BPF state machine, but only the - // parts that can actually be generated by our BPF compiler. If this code - // is used for purposes other than verifying the output of the sandbox's - // BPF compiler, we might have to extend this BPF interpreter. - static uint32_t EvaluateBPF(const std::vector<struct sock_filter>& program, - const struct arch_seccomp_data& data, - const char** err); - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(Verifier); -}; - -} // namespace bpf_dsl -} // namespace sandbox - -#endif // SANDBOX_LINUX_BPF_DSL_VERIFIER_H__ |