Home | History | Annotate | Download | only in bpf_dsl
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
      6 #define SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
      7 
      8 #include <stddef.h>
      9 #include <stdint.h>
     10 
     11 #include <utility>
     12 #include <vector>
     13 
     14 #include "base/macros.h"
     15 #include "base/memory/ref_counted.h"
     16 #include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
     17 #include "sandbox/linux/bpf_dsl/cons.h"
     18 #include "sandbox/linux/bpf_dsl/trap_registry.h"
     19 #include "sandbox/sandbox_export.h"
     20 
     21 // The sandbox::bpf_dsl namespace provides a domain-specific language
     22 // to make writing BPF policies more expressive.  In general, the
     23 // object types all have value semantics (i.e., they can be copied
     24 // around, returned from or passed to function calls, etc. without any
     25 // surprising side effects), though not all support assignment.
     26 //
     27 // An idiomatic and demonstrative (albeit silly) example of this API
     28 // would be:
     29 //
     30 //      #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
     31 //
     32 //      using namespace sandbox::bpf_dsl;
     33 //
     34 //      class SillyPolicy : public Policy {
     35 //       public:
     36 //        SillyPolicy() {}
     37 //        ~SillyPolicy() override {}
     38 //        ResultExpr EvaluateSyscall(int sysno) const override {
     39 //          if (sysno == __NR_fcntl) {
     40 //            Arg<int> fd(0), cmd(1);
     41 //            Arg<unsigned long> flags(2);
     42 //            const uint64_t kGoodFlags = O_ACCMODE | O_NONBLOCK;
     43 //            return If(AllOf(fd == 0,
     44 //                            cmd == F_SETFL,
     45 //                            (flags & ~kGoodFlags) == 0),
     46 //                      Allow())
     47 //                .ElseIf(AnyOf(cmd == F_DUPFD, cmd == F_DUPFD_CLOEXEC),
     48 //                        Error(EMFILE))
     49 //                .Else(Trap(SetFlagHandler, NULL));
     50 //          } else {
     51 //            return Allow();
     52 //          }
     53 //        }
     54 //
     55 //       private:
     56 //        DISALLOW_COPY_AND_ASSIGN(SillyPolicy);
     57 //      };
     58 //
     59 // More generally, the DSL currently supports the following grammar:
     60 //
     61 //   result = Allow() | Error(errno) | Kill() | Trace(aux)
     62 //          | Trap(trap_func, aux) | UnsafeTrap(trap_func, aux)
     63 //          | If(bool, result)[.ElseIf(bool, result)].Else(result)
     64 //          | Switch(arg)[.Case(val, result)].Default(result)
     65 //   bool   = BoolConst(boolean) | Not(bool) | AllOf(bool...) | AnyOf(bool...)
     66 //          | arg == val | arg != val
     67 //   arg    = Arg<T>(num) | arg & mask
     68 //
     69 // The semantics of each function and operator are intended to be
     70 // intuitive, but are described in more detail below.
     71 //
     72 // (Credit to Sean Parent's "Inheritance is the Base Class of Evil"
     73 // talk at Going Native 2013 for promoting value semantics via shared
     74 // pointers to immutable state.)
     75 
     76 namespace sandbox {
     77 namespace bpf_dsl {
     78 
     79 // ResultExpr is an opaque reference to an immutable result expression tree.
     80 typedef scoped_refptr<const internal::ResultExprImpl> ResultExpr;
     81 
     82 // BoolExpr is an opaque reference to an immutable boolean expression tree.
     83 typedef scoped_refptr<const internal::BoolExprImpl> BoolExpr;
     84 
     85 // Allow specifies a result that the system call should be allowed to
     86 // execute normally.
     87 SANDBOX_EXPORT ResultExpr Allow();
     88 
     89 // Error specifies a result that the system call should fail with
     90 // error number |err|.  As a special case, Error(0) will result in the
     91 // system call appearing to have succeeded, but without having any
     92 // side effects.
     93 SANDBOX_EXPORT ResultExpr Error(int err);
     94 
     95 // Kill specifies a result to kill the process (task) immediately.
     96 SANDBOX_EXPORT ResultExpr Kill();
     97 
     98 // Trace specifies a result to notify a tracing process via the
     99 // PTRACE_EVENT_SECCOMP event and allow it to change or skip the system call.
    100 // The value of |aux| will be available to the tracer via PTRACE_GETEVENTMSG.
    101 SANDBOX_EXPORT ResultExpr Trace(uint16_t aux);
    102 
    103 // Trap specifies a result that the system call should be handled by
    104 // trapping back into userspace and invoking |trap_func|, passing
    105 // |aux| as the second parameter.
    106 SANDBOX_EXPORT ResultExpr
    107     Trap(TrapRegistry::TrapFnc trap_func, const void* aux);
    108 
    109 // UnsafeTrap is like Trap, except the policy is marked as "unsafe"
    110 // and allowed to use SandboxSyscall to invoke any system call.
    111 //
    112 // NOTE: This feature, by definition, disables all security features of
    113 //   the sandbox. It should never be used in production, but it can be
    114 //   very useful to diagnose code that is incompatible with the sandbox.
    115 //   If even a single system call returns "UnsafeTrap", the security of
    116 //   entire sandbox should be considered compromised.
    117 SANDBOX_EXPORT ResultExpr
    118     UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux);
    119 
    120 // BoolConst converts a bool value into a BoolExpr.
    121 SANDBOX_EXPORT BoolExpr BoolConst(bool value);
    122 
    123 // Not returns a BoolExpr representing the logical negation of |cond|.
    124 SANDBOX_EXPORT BoolExpr Not(const BoolExpr& cond);
    125 
    126 // AllOf returns a BoolExpr representing the logical conjunction ("and")
    127 // of zero or more BoolExprs.
    128 SANDBOX_EXPORT BoolExpr AllOf();
    129 SANDBOX_EXPORT BoolExpr AllOf(const BoolExpr& lhs, const BoolExpr& rhs);
    130 template <typename... Rest>
    131 SANDBOX_EXPORT BoolExpr AllOf(const BoolExpr& first, const Rest&... rest);
    132 
    133 // AnyOf returns a BoolExpr representing the logical disjunction ("or")
    134 // of zero or more BoolExprs.
    135 SANDBOX_EXPORT BoolExpr AnyOf();
    136 SANDBOX_EXPORT BoolExpr AnyOf(const BoolExpr& lhs, const BoolExpr& rhs);
    137 template <typename... Rest>
    138 SANDBOX_EXPORT BoolExpr AnyOf(const BoolExpr& first, const Rest&... rest);
    139 
    140 template <typename T>
    141 class SANDBOX_EXPORT Arg {
    142  public:
    143   // Initializes the Arg to represent the |num|th system call
    144   // argument (indexed from 0), which is of type |T|.
    145   explicit Arg(int num);
    146 
    147   Arg(const Arg& arg) : num_(arg.num_), mask_(arg.mask_) {}
    148 
    149   // Returns an Arg representing the current argument, but after
    150   // bitwise-and'ing it with |rhs|.
    151   friend Arg operator&(const Arg& lhs, uint64_t rhs) {
    152     return Arg(lhs.num_, lhs.mask_ & rhs);
    153   }
    154 
    155   // Returns a boolean expression comparing whether the system call argument
    156   // (after applying any bitmasks, if appropriate) equals |rhs|.
    157   friend BoolExpr operator==(const Arg& lhs, T rhs) { return lhs.EqualTo(rhs); }
    158 
    159   // Returns a boolean expression comparing whether the system call argument
    160   // (after applying any bitmasks, if appropriate) does not equal |rhs|.
    161   friend BoolExpr operator!=(const Arg& lhs, T rhs) { return Not(lhs == rhs); }
    162 
    163  private:
    164   Arg(int num, uint64_t mask) : num_(num), mask_(mask) {}
    165 
    166   BoolExpr EqualTo(T val) const;
    167 
    168   int num_;
    169   uint64_t mask_;
    170 
    171   DISALLOW_ASSIGN(Arg);
    172 };
    173 
    174 // If begins a conditional result expression predicated on the
    175 // specified boolean expression.
    176 SANDBOX_EXPORT Elser If(const BoolExpr& cond, const ResultExpr& then_result);
    177 
    178 class SANDBOX_EXPORT Elser {
    179  public:
    180   Elser(const Elser& elser);
    181   ~Elser();
    182 
    183   // ElseIf extends the conditional result expression with another
    184   // "if then" clause, predicated on the specified boolean expression.
    185   Elser ElseIf(const BoolExpr& cond, const ResultExpr& then_result) const;
    186 
    187   // Else terminates a conditional result expression using |else_result| as
    188   // the default fallback result expression.
    189   ResultExpr Else(const ResultExpr& else_result) const;
    190 
    191  private:
    192   typedef std::pair<BoolExpr, ResultExpr> Clause;
    193 
    194   explicit Elser(cons::List<Clause> clause_list);
    195 
    196   cons::List<Clause> clause_list_;
    197 
    198   friend Elser If(const BoolExpr&, const ResultExpr&);
    199   template <typename T>
    200   friend Caser<T> Switch(const Arg<T>&);
    201   DISALLOW_ASSIGN(Elser);
    202 };
    203 
    204 // Switch begins a switch expression dispatched according to the
    205 // specified argument value.
    206 template <typename T>
    207 SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg);
    208 
    209 template <typename T>
    210 class SANDBOX_EXPORT Caser {
    211  public:
    212   Caser(const Caser<T>& caser) : arg_(caser.arg_), elser_(caser.elser_) {}
    213   ~Caser() {}
    214 
    215   // Case adds a single-value "case" clause to the switch.
    216   Caser<T> Case(T value, const ResultExpr& result) const;
    217 
    218   // Cases adds a multiple-value "case" clause to the switch.
    219   // See also the SANDBOX_BPF_DSL_CASES macro below for a more idiomatic way
    220   // of using this function.
    221   template <typename... Values>
    222   Caser<T> CasesImpl(const ResultExpr& result, const Values&... values) const;
    223 
    224   // Terminate the switch with a "default" clause.
    225   ResultExpr Default(const ResultExpr& result) const;
    226 
    227  private:
    228   Caser(const Arg<T>& arg, Elser elser) : arg_(arg), elser_(elser) {}
    229 
    230   Arg<T> arg_;
    231   Elser elser_;
    232 
    233   template <typename U>
    234   friend Caser<U> Switch(const Arg<U>&);
    235   DISALLOW_ASSIGN(Caser);
    236 };
    237 
    238 // Recommended usage is to put
    239 //    #define CASES SANDBOX_BPF_DSL_CASES
    240 // near the top of the .cc file (e.g., nearby any "using" statements), then
    241 // use like:
    242 //    Switch(arg).CASES((3, 5, 7), result)...;
    243 #define SANDBOX_BPF_DSL_CASES(values, result) \
    244   CasesImpl(result, SANDBOX_BPF_DSL_CASES_HELPER values)
    245 
    246 // Helper macro to strip parentheses.
    247 #define SANDBOX_BPF_DSL_CASES_HELPER(...) __VA_ARGS__
    248 
    249 // =====================================================================
    250 // Official API ends here.
    251 // =====================================================================
    252 
    253 namespace internal {
    254 
    255 // Make argument-dependent lookup work.  This is necessary because although
    256 // BoolExpr is defined in bpf_dsl, since it's merely a typedef for
    257 // scoped_refptr<const internal::BoolExplImpl>, argument-dependent lookup only
    258 // searches the "internal" nested namespace.
    259 using bpf_dsl::Not;
    260 using bpf_dsl::AllOf;
    261 using bpf_dsl::AnyOf;
    262 
    263 // Returns a boolean expression that represents whether system call
    264 // argument |num| of size |size| is equal to |val|, when masked
    265 // according to |mask|.  Users should use the Arg template class below
    266 // instead of using this API directly.
    267 SANDBOX_EXPORT BoolExpr
    268     ArgEq(int num, size_t size, uint64_t mask, uint64_t val);
    269 
    270 // Returns the default mask for a system call argument of the specified size.
    271 SANDBOX_EXPORT uint64_t DefaultMask(size_t size);
    272 
    273 }  // namespace internal
    274 
    275 template <typename T>
    276 Arg<T>::Arg(int num)
    277     : num_(num), mask_(internal::DefaultMask(sizeof(T))) {
    278 }
    279 
    280 // Definition requires ArgEq to have been declared.  Moved out-of-line
    281 // to minimize how much internal clutter users have to ignore while
    282 // reading the header documentation.
    283 //
    284 // Additionally, we use this helper member function to avoid linker errors
    285 // caused by defining operator== out-of-line.  For a more detailed explanation,
    286 // see http://www.parashift.com/c++-faq-lite/template-friends.html.
    287 template <typename T>
    288 BoolExpr Arg<T>::EqualTo(T val) const {
    289   if (sizeof(T) == 4) {
    290     // Prevent sign-extension of negative int32_t values.
    291     return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint32_t>(val));
    292   }
    293   return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint64_t>(val));
    294 }
    295 
    296 template <typename T>
    297 SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg) {
    298   return Caser<T>(arg, Elser(nullptr));
    299 }
    300 
    301 template <typename T>
    302 Caser<T> Caser<T>::Case(T value, const ResultExpr& result) const {
    303   return SANDBOX_BPF_DSL_CASES((value), result);
    304 }
    305 
    306 template <typename T>
    307 template <typename... Values>
    308 Caser<T> Caser<T>::CasesImpl(const ResultExpr& result,
    309                              const Values&... values) const {
    310   // Theoretically we could evaluate arg_ just once and emit a more efficient
    311   // dispatch table, but for now we simply translate into an equivalent
    312   // If/ElseIf/Else chain.
    313 
    314   return Caser<T>(arg_, elser_.ElseIf(AnyOf((arg_ == values)...), result));
    315 }
    316 
    317 template <typename T>
    318 ResultExpr Caser<T>::Default(const ResultExpr& result) const {
    319   return elser_.Else(result);
    320 }
    321 
    322 template <typename... Rest>
    323 BoolExpr AllOf(const BoolExpr& first, const Rest&... rest) {
    324   return AllOf(first, AllOf(rest...));
    325 }
    326 
    327 template <typename... Rest>
    328 BoolExpr AnyOf(const BoolExpr& first, const Rest&... rest) {
    329   return AnyOf(first, AnyOf(rest...));
    330 }
    331 
    332 }  // namespace bpf_dsl
    333 }  // namespace sandbox
    334 
    335 #endif  // SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
    336