 Chromium Code Reviews
 Chromium Code Reviews Issue 299743002:
  Add domain-specific language for BPF policies  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src
    
  
    Issue 299743002:
  Add domain-specific language for BPF policies  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src| Index: sandbox/linux/seccomp-bpf-helpers/bpf_dsl.h | 
| diff --git a/sandbox/linux/seccomp-bpf-helpers/bpf_dsl.h b/sandbox/linux/seccomp-bpf-helpers/bpf_dsl.h | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..4c2f913c522cc5523f05a21c1206170e9896d51a | 
| --- /dev/null | 
| +++ b/sandbox/linux/seccomp-bpf-helpers/bpf_dsl.h | 
| @@ -0,0 +1,259 @@ | 
| +// 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_SECCOMP_BPF_HELPERS_BPF_DSL_H_ | 
| +#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BPF_DSL_H_ | 
| + | 
| +#include <stdint.h> | 
| + | 
| +#include "base/macros.h" | 
| +#include "base/memory/ref_counted.h" | 
| +#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" | 
| +#include "sandbox/linux/seccomp-bpf/trap.h" | 
| +#include "sandbox/sandbox_export.h" | 
| + | 
| +namespace sandbox { | 
| +class ErrorCode; | 
| +class SandboxBPF; | 
| +} | 
| + | 
| +// 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/seccomp-bpf-helpers/bpf_dsl.h" | 
| +// | 
| +// using namespace sandbox::bpf_dsl; | 
| +// | 
| +// class SillyPolicy : public SandboxBPFDSLPolicy { | 
| +// public: | 
| +// SillyPolicy() {} | 
| +// virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE { | 
| +// if (sysno == __NR_fcntl) { | 
| +// Arg<int> fd(0), cmd(1); | 
| +// Arg<unsigned long> flags(2); | 
| +// const unsigned long kBadFlags = ~(O_ACCMODE|O_NONBLOCK); | 
| +// return If(fd == 0 && cmd == F_SETFL && | 
| +// (flags & kBadFlags) == 0).Then( | 
| +// Allow() | 
| +// ).ElseIf(cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC).Then( | 
| +// Error(EMFILE) | 
| +// ).Else( | 
| +// Trap(SetFlagHandler, NULL) | 
| +// ); | 
| +// } | 
| 
jln (very slow on Chromium)
2014/06/24 00:03:52
nit: else {} so that the compiler checks that the
 
mdempsky
2014/06/24 00:55:40
Done.
 | 
| +// return Allow(); | 
| +// } | 
| +// private: | 
| +// DISALLOW_COPY_AND_ASSIGN(SillyPolicy); | 
| +// }; | 
| +// | 
| +// More generally, the DSL currently supports the following grammar: | 
| +// | 
| +// result = Allow() | Error(errno) | Trap(trap_func, arg) | 
| +// | If(bool).Then(result)[.ElseIf(bool).Then(result)].Else(result) | 
| +// bool = arg == val | (arg & mask) == mask | (arg & mask) == 0 | 
| +// | !bool | bool && bool | bool || bool | 
| +// | 
| +// 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 { | 
| + | 
| +// Forward declarations of classes; see below for proper documentation. | 
| +class Thener; | 
| +class Elser; | 
| +namespace internal { | 
| +class ResultExprImpl; | 
| +class BoolExprImpl; | 
| +struct IfThen; | 
| +typedef scoped_refptr<const IfThen> IfThenList; | 
| +} | 
| + | 
| +// ResultExpr is an opaque reference to an immutable result expression tree. | 
| +typedef scoped_refptr<const internal::ResultExprImpl> ResultExpr; | 
| + | 
| +// 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); | 
| + | 
| +// 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(::sandbox::Trap::TrapFnc trap_func, void* aux); | 
| + | 
| +// BoolExpr is an opaque reference to an immutable boolean expression tree. | 
| +typedef scoped_refptr<const internal::BoolExprImpl> BoolExpr; | 
| + | 
| +template <typename T> | 
| +class SANDBOX_EXPORT Arg { | 
| + public: | 
| + // Initializes the Arg to represent the |num|th system call | 
| + // argument, which is of type |T|. | 
| + explicit Arg(int num) : num_(num), mask_(-1) {} | 
| + | 
| + 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|. | 
| + Arg operator&(uint64_t rhs) const { return Arg(num_, mask_ & rhs); } | 
| + | 
| + // Returns a boolean expression comparing whether the system call | 
| + // argument (after applying any bitmasks, if appropriate) equals |rhs|. | 
| + BoolExpr operator==(T rhs) const; | 
| + | 
| + private: | 
| + Arg(int num, uint64_t mask) : num_(num), mask_(mask) {} | 
| + int num_; | 
| + uint64_t mask_; | 
| + DISALLOW_ASSIGN(Arg); | 
| +}; | 
| + | 
| +// Various ways to combine boolean expressions into more complex expressions. | 
| +// They follow standard boolean algebra laws. | 
| +SANDBOX_EXPORT BoolExpr operator!(BoolExpr cond); | 
| +SANDBOX_EXPORT BoolExpr operator&&(BoolExpr lhs, BoolExpr rhs); | 
| +SANDBOX_EXPORT BoolExpr operator||(BoolExpr lhs, BoolExpr rhs); | 
| + | 
| +// If begins a conditional result expression predicated on the | 
| +// specified boolean expression. | 
| +SANDBOX_EXPORT Thener If(BoolExpr cond); | 
| + | 
| +class SANDBOX_EXPORT Thener { | 
| + public: | 
| + Thener(const Thener& thener); | 
| + ~Thener(); | 
| + | 
| + // Then associates the result expression |then_result| with the most recent | 
| + // If or ElseIf clause's boolean expression. | 
| + Elser Then(ResultExpr then_result) const; | 
| + | 
| + private: | 
| + Thener(internal::IfThenList if_then_list, BoolExpr cond); | 
| + internal::IfThenList if_then_list_; | 
| + BoolExpr cond_; | 
| + friend Thener If(BoolExpr); | 
| + friend class Elser; | 
| + DISALLOW_ASSIGN(Thener); | 
| +}; | 
| + | 
| +class SANDBOX_EXPORT Elser { | 
| + public: | 
| + Elser(const Elser& elser); | 
| + ~Elser(); | 
| + | 
| + // Else terminates a conditional result expression using |else_result| as | 
| + // the default fallback result expression. | 
| + ResultExpr Else(ResultExpr else_result) const; | 
| + | 
| + // ElseIf extends the conditional result expression with another | 
| + // clause, predicated on the specified boolean expression. | 
| + Thener ElseIf(BoolExpr cond) const; | 
| + | 
| + private: | 
| + Elser(internal::IfThenList if_then_list); | 
| + internal::IfThenList if_then_list_; | 
| + friend class Thener; | 
| + DISALLOW_ASSIGN(Elser); | 
| +}; | 
| + | 
| +// Helper class to make writing policies easier. | 
| 
jln (very slow on Chromium)
2014/06/24 00:03:52
Let's have this earlier in the file as it is one o
 
mdempsky
2014/06/24 00:55:40
Done.
 | 
| +class SANDBOX_EXPORT SandboxBPFDSLPolicy : public SandboxBPFPolicy { | 
| + public: | 
| + SandboxBPFDSLPolicy() : SandboxBPFPolicy() {} | 
| + | 
| + // User extension point for writing custom sandbox policies. | 
| + 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; | 
| + | 
| + // Override implementations from SandboxBPFPolicy. Marked as FINAL | 
| + // to prevent mixups with child classes accidentally overloading | 
| + // these instead of the above methods. | 
| + virtual ErrorCode EvaluateSyscall(SandboxBPF* sb, | 
| + int sysno) const OVERRIDE FINAL; | 
| + virtual ErrorCode InvalidSyscall(SandboxBPF* sb) const OVERRIDE FINAL; | 
| + | 
| + // Helper method so policies can just write Trap(func, aux). | 
| + static ResultExpr Trap(::sandbox::Trap::TrapFnc trap_func, void* aux) { | 
| + return bpf_dsl::Trap(trap_func, aux); | 
| + } | 
| + | 
| + private: | 
| + DISALLOW_COPY_AND_ASSIGN(SandboxBPFDSLPolicy); | 
| +}; | 
| + | 
| +namespace internal { | 
| + | 
| +// 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); | 
| + | 
| +// Internal interface implemented by BoolExpr implementations. | 
| +class SANDBOX_EXPORT BoolExprImpl : public base::RefCounted<BoolExprImpl> { | 
| + public: | 
| + BoolExprImpl() {} | 
| + virtual ErrorCode Compile(SandboxBPF* sb, | 
| + ErrorCode true_ec, | 
| + ErrorCode false_ec) const = 0; | 
| + | 
| + protected: | 
| + virtual ~BoolExprImpl() {} | 
| + | 
| + private: | 
| + friend class base::RefCounted<BoolExprImpl>; | 
| + DISALLOW_COPY_AND_ASSIGN(BoolExprImpl); | 
| +}; | 
| + | 
| +// Internal interface implemented by ResultExpr implementations. | 
| +class SANDBOX_EXPORT ResultExprImpl : public base::RefCounted<ResultExprImpl> { | 
| + public: | 
| + ResultExprImpl() {} | 
| + virtual ErrorCode Compile(SandboxBPF* sb) const = 0; | 
| + | 
| + protected: | 
| + virtual ~ResultExprImpl() {} | 
| + | 
| + private: | 
| + friend class base::RefCounted<ResultExprImpl>; | 
| + DISALLOW_COPY_AND_ASSIGN(ResultExprImpl); | 
| +}; | 
| + | 
| +} // namespace internal | 
| + | 
| +// 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. | 
| +template <typename T> | 
| +BoolExpr Arg<T>::operator==(T rhs) const { | 
| + return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint64_t>(rhs)); | 
| +} | 
| + | 
| +} // namespace bpf_dsl | 
| + | 
| +} // namespace sandbox | 
| + | 
| +#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BPF_DSL_H_ |