• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <stdint.h>
9 
10 #include <utility>
11 #include <vector>
12 
13 #include "base/macros.h"
14 #include "base/memory/ref_counted.h"
15 #include "sandbox/linux/bpf_dsl/cons.h"
16 #include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h"
17 #include "sandbox/linux/seccomp-bpf/trap.h"
18 #include "sandbox/sandbox_export.h"
19 
20 namespace sandbox {
21 class ErrorCode;
22 class SandboxBPF;
23 }
24 
25 // The sandbox::bpf_dsl namespace provides a domain-specific language
26 // to make writing BPF policies more expressive.  In general, the
27 // object types all have value semantics (i.e., they can be copied
28 // around, returned from or passed to function calls, etc. without any
29 // surprising side effects), though not all support assignment.
30 //
31 // An idiomatic and demonstrative (albeit silly) example of this API
32 // would be:
33 //
34 //      #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
35 //
36 //      using namespace sandbox::bpf_dsl;
37 //
38 //      class SillyPolicy : public SandboxBPFDSLPolicy {
39 //       public:
40 //        SillyPolicy() {}
41 //        virtual ~SillyPolicy() {}
42 //        virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
43 //          if (sysno == __NR_fcntl) {
44 //            Arg<int> fd(0), cmd(1);
45 //            Arg<unsigned long> flags(2);
46 //            const uint64_t kGoodFlags = O_ACCMODE | O_NONBLOCK;
47 //            return If(fd == 0 && cmd == F_SETFL && (flags & ~kGoodFlags) == 0,
48 //                      Allow())
49 //                .ElseIf(cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC,
50 //                        Error(EMFILE))
51 //                .Else(Trap(SetFlagHandler, NULL));
52 //          } else {
53 //            return Allow();
54 //          }
55 //        }
56 //
57 //       private:
58 //        DISALLOW_COPY_AND_ASSIGN(SillyPolicy);
59 //      };
60 //
61 // More generally, the DSL currently supports the following grammar:
62 //
63 //   result = Allow() | Error(errno) | Kill(msg) | Trace(aux)
64 //          | Trap(trap_func, aux) | UnsafeTrap(trap_func, aux)
65 //          | If(bool, result)[.ElseIf(bool, result)].Else(result)
66 //          | Switch(arg)[.Case(val, result)].Default(result)
67 //   bool   = BoolConst(boolean) | !bool | bool && bool | bool || bool
68 //          | arg == val | arg != val
69 //   arg    = Arg<T>(num) | arg & mask
70 //
71 // The semantics of each function and operator are intended to be
72 // intuitive, but are described in more detail below.
73 //
74 // (Credit to Sean Parent's "Inheritance is the Base Class of Evil"
75 // talk at Going Native 2013 for promoting value semantics via shared
76 // pointers to immutable state.)
77 
78 namespace sandbox {
79 namespace bpf_dsl {
80 
81 // Forward declarations of classes; see below for proper documentation.
82 class Elser;
83 template <typename T>
84 class Caser;
85 namespace internal {
86 class ResultExprImpl;
87 class BoolExprImpl;
88 }
89 
90 // ResultExpr is an opaque reference to an immutable result expression tree.
91 typedef scoped_refptr<const internal::ResultExprImpl> ResultExpr;
92 
93 // BoolExpr is an opaque reference to an immutable boolean expression tree.
94 typedef scoped_refptr<const internal::BoolExprImpl> BoolExpr;
95 
96 // Helper class to make writing policies easier.
97 class SANDBOX_EXPORT SandboxBPFDSLPolicy : public SandboxBPFPolicy {
98  public:
SandboxBPFDSLPolicy()99   SandboxBPFDSLPolicy() : SandboxBPFPolicy() {}
~SandboxBPFDSLPolicy()100   virtual ~SandboxBPFDSLPolicy() {}
101 
102   // User extension point for writing custom sandbox policies.
103   virtual ResultExpr EvaluateSyscall(int sysno) const = 0;
104 
105   // Optional overload for specifying alternate behavior for invalid
106   // system calls.  The default is to return ENOSYS.
107   virtual ResultExpr InvalidSyscall() const;
108 
109   // Override implementations from SandboxBPFPolicy.  Marked as FINAL
110   // to prevent mixups with child classes accidentally overloading
111   // these instead of the above methods.
112   virtual ErrorCode EvaluateSyscall(SandboxBPF* sb,
113                                     int sysno) const OVERRIDE FINAL;
114   virtual ErrorCode InvalidSyscall(SandboxBPF* sb) const OVERRIDE FINAL;
115 
116   // Helper method so policies can just write Trap(func, aux).
117   static ResultExpr Trap(Trap::TrapFnc trap_func, const void* aux);
118 
119  private:
120   DISALLOW_COPY_AND_ASSIGN(SandboxBPFDSLPolicy);
121 };
122 
123 // Allow specifies a result that the system call should be allowed to
124 // execute normally.
125 SANDBOX_EXPORT ResultExpr Allow();
126 
127 // Error specifies a result that the system call should fail with
128 // error number |err|.  As a special case, Error(0) will result in the
129 // system call appearing to have succeeded, but without having any
130 // side effects.
131 SANDBOX_EXPORT ResultExpr Error(int err);
132 
133 // Kill specifies a result to kill the program and print an error message.
134 SANDBOX_EXPORT ResultExpr Kill(const char* msg);
135 
136 // Trace specifies a result to notify a tracing process via the
137 // PTRACE_EVENT_SECCOMP event and allow it to change or skip the system call.
138 // The value of |aux| will be available to the tracer via PTRACE_GETEVENTMSG.
139 SANDBOX_EXPORT ResultExpr Trace(uint16_t aux);
140 
141 // Trap specifies a result that the system call should be handled by
142 // trapping back into userspace and invoking |trap_func|, passing
143 // |aux| as the second parameter.
144 SANDBOX_EXPORT ResultExpr Trap(Trap::TrapFnc trap_func, const void* aux);
145 
146 // UnsafeTrap is like Trap, except the policy is marked as "unsafe"
147 // and allowed to use SandboxSyscall to invoke any system call.
148 //
149 // NOTE: This feature, by definition, disables all security features of
150 //   the sandbox. It should never be used in production, but it can be
151 //   very useful to diagnose code that is incompatible with the sandbox.
152 //   If even a single system call returns "UnsafeTrap", the security of
153 //   entire sandbox should be considered compromised.
154 SANDBOX_EXPORT ResultExpr UnsafeTrap(Trap::TrapFnc trap_func, const void* aux);
155 
156 // BoolConst converts a bool value into a BoolExpr.
157 SANDBOX_EXPORT BoolExpr BoolConst(bool value);
158 
159 // Various ways to combine boolean expressions into more complex expressions.
160 // They follow standard boolean algebra laws.
161 SANDBOX_EXPORT BoolExpr operator!(const BoolExpr& cond);
162 SANDBOX_EXPORT BoolExpr operator&&(const BoolExpr& lhs, const BoolExpr& rhs);
163 SANDBOX_EXPORT BoolExpr operator||(const BoolExpr& lhs, const BoolExpr& rhs);
164 
165 template <typename T>
166 class SANDBOX_EXPORT Arg {
167  public:
168   // Initializes the Arg to represent the |num|th system call
169   // argument (indexed from 0), which is of type |T|.
170   explicit Arg(int num);
171 
Arg(const Arg & arg)172   Arg(const Arg& arg) : num_(arg.num_), mask_(arg.mask_) {}
173 
174   // Returns an Arg representing the current argument, but after
175   // bitwise-and'ing it with |rhs|.
176   friend Arg operator&(const Arg& lhs, uint64_t rhs) {
177     return Arg(lhs.num_, lhs.mask_ & rhs);
178   }
179 
180   // Returns a boolean expression comparing whether the system call argument
181   // (after applying any bitmasks, if appropriate) equals |rhs|.
182   friend BoolExpr operator==(const Arg& lhs, T rhs) { return lhs.EqualTo(rhs); }
183 
184   // Returns a boolean expression comparing whether the system call argument
185   // (after applying any bitmasks, if appropriate) does not equal |rhs|.
186   friend BoolExpr operator!=(const Arg& lhs, T rhs) { return !(lhs == rhs); }
187 
188  private:
Arg(int num,uint64_t mask)189   Arg(int num, uint64_t mask) : num_(num), mask_(mask) {}
190 
191   BoolExpr EqualTo(T val) const;
192 
193   int num_;
194   uint64_t mask_;
195 
196   DISALLOW_ASSIGN(Arg);
197 };
198 
199 // If begins a conditional result expression predicated on the
200 // specified boolean expression.
201 SANDBOX_EXPORT Elser If(const BoolExpr& cond, const ResultExpr& then_result);
202 
203 class SANDBOX_EXPORT Elser {
204  public:
205   Elser(const Elser& elser);
206   ~Elser();
207 
208   // ElseIf extends the conditional result expression with another
209   // "if then" clause, predicated on the specified boolean expression.
210   Elser ElseIf(const BoolExpr& cond, const ResultExpr& then_result) const;
211 
212   // Else terminates a conditional result expression using |else_result| as
213   // the default fallback result expression.
214   ResultExpr Else(const ResultExpr& else_result) const;
215 
216  private:
217   typedef std::pair<BoolExpr, ResultExpr> Clause;
218 
219   explicit Elser(Cons<Clause>::List clause_list);
220 
221   Cons<Clause>::List clause_list_;
222 
223   friend Elser If(const BoolExpr&, const ResultExpr&);
224   template <typename T>
225   friend Caser<T> Switch(const Arg<T>&);
226   DISALLOW_ASSIGN(Elser);
227 };
228 
229 // Switch begins a switch expression dispatched according to the
230 // specified argument value.
231 template <typename T>
232 SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg);
233 
234 template <typename T>
235 class SANDBOX_EXPORT Caser {
236  public:
Caser(const Caser<T> & caser)237   Caser(const Caser<T>& caser) : arg_(caser.arg_), elser_(caser.elser_) {}
~Caser()238   ~Caser() {}
239 
240   // Case adds a single-value "case" clause to the switch.
241   Caser<T> Case(T value, ResultExpr result) const;
242 
243   // Cases adds a multiple-value "case" clause to the switch.
244   // See also the SANDBOX_BPF_DSL_CASES macro below for a more idiomatic way
245   // of using this function.
246   Caser<T> Cases(const std::vector<T>& values, ResultExpr result) const;
247 
248   // Terminate the switch with a "default" clause.
249   ResultExpr Default(ResultExpr result) const;
250 
251  private:
Caser(const Arg<T> & arg,Elser elser)252   Caser(const Arg<T>& arg, Elser elser) : arg_(arg), elser_(elser) {}
253 
254   Arg<T> arg_;
255   Elser elser_;
256 
257   template <typename U>
258   friend Caser<U> Switch(const Arg<U>&);
259   DISALLOW_ASSIGN(Caser);
260 };
261 
262 // Recommended usage is to put
263 //    #define CASES SANDBOX_BPF_DSL_CASES
264 // near the top of the .cc file (e.g., nearby any "using" statements), then
265 // use like:
266 //    Switch(arg).CASES((3, 5, 7), result)...;
267 #define SANDBOX_BPF_DSL_CASES(values, result) \
268   Cases(SANDBOX_BPF_DSL_CASES_HELPER values, result)
269 
270 // Helper macro to construct a std::vector from an initializer list.
271 // TODO(mdempsky): Convert to use C++11 initializer lists instead.
272 #define SANDBOX_BPF_DSL_CASES_HELPER(value, ...)                           \
273   ({                                                                       \
274     const __typeof__(value) bpf_dsl_cases_values[] = {value, __VA_ARGS__}; \
275     std::vector<__typeof__(value)>(                                        \
276         bpf_dsl_cases_values,                                              \
277         bpf_dsl_cases_values + arraysize(bpf_dsl_cases_values));           \
278   })
279 
280 // =====================================================================
281 // Official API ends here.
282 // =====================================================================
283 
284 // Definitions below are necessary here only for C++03 compatibility.
285 // Once C++11 is available, they should be moved into bpf_dsl.cc via extern
286 // templates.
287 namespace internal {
288 
289 // Make argument-dependent lookup work.  This is necessary because although
290 // BoolExpr is defined in bpf_dsl, since it's merely a typedef for
291 // scoped_refptr<const internal::BoolExplImpl>, argument-dependent lookup only
292 // searches the "internal" nested namespace.
293 using bpf_dsl::operator!;
294 using bpf_dsl::operator||;
295 using bpf_dsl::operator&&;
296 
297 // Returns a boolean expression that represents whether system call
298 // argument |num| of size |size| is equal to |val|, when masked
299 // according to |mask|.  Users should use the Arg template class below
300 // instead of using this API directly.
301 SANDBOX_EXPORT BoolExpr
302     ArgEq(int num, size_t size, uint64_t mask, uint64_t val);
303 
304 // Returns the default mask for a system call argument of the specified size.
305 SANDBOX_EXPORT uint64_t DefaultMask(size_t size);
306 
307 // Internal interface implemented by BoolExpr implementations.
308 class SANDBOX_EXPORT BoolExprImpl : public base::RefCounted<BoolExprImpl> {
309  public:
BoolExprImpl()310   BoolExprImpl() {}
311   virtual ErrorCode Compile(SandboxBPF* sb,
312                             ErrorCode true_ec,
313                             ErrorCode false_ec) const = 0;
314 
315  protected:
~BoolExprImpl()316   virtual ~BoolExprImpl() {}
317 
318  private:
319   friend class base::RefCounted<BoolExprImpl>;
320   DISALLOW_COPY_AND_ASSIGN(BoolExprImpl);
321 };
322 
323 // Internal interface implemented by ResultExpr implementations.
324 class SANDBOX_EXPORT ResultExprImpl : public base::RefCounted<ResultExprImpl> {
325  public:
ResultExprImpl()326   ResultExprImpl() {}
327   virtual ErrorCode Compile(SandboxBPF* sb) const = 0;
328 
329  protected:
~ResultExprImpl()330   virtual ~ResultExprImpl() {}
331 
332  private:
333   friend class base::RefCounted<ResultExprImpl>;
334   DISALLOW_COPY_AND_ASSIGN(ResultExprImpl);
335 };
336 
337 }  // namespace internal
338 
339 template <typename T>
Arg(int num)340 Arg<T>::Arg(int num)
341     : num_(num), mask_(internal::DefaultMask(sizeof(T))) {
342 }
343 
344 // Definition requires ArgEq to have been declared.  Moved out-of-line
345 // to minimize how much internal clutter users have to ignore while
346 // reading the header documentation.
347 //
348 // Additionally, we use this helper member function to avoid linker errors
349 // caused by defining operator== out-of-line.  For a more detailed explanation,
350 // see http://www.parashift.com/c++-faq-lite/template-friends.html.
351 template <typename T>
EqualTo(T val)352 BoolExpr Arg<T>::EqualTo(T val) const {
353   return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint64_t>(val));
354 }
355 
356 template <typename T>
Switch(const Arg<T> & arg)357 SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg) {
358   return Caser<T>(arg, Elser(Cons<Elser::Clause>::List()));
359 }
360 
361 template <typename T>
Case(T value,ResultExpr result)362 Caser<T> Caser<T>::Case(T value, ResultExpr result) const {
363   return SANDBOX_BPF_DSL_CASES((value), result);
364 }
365 
366 template <typename T>
Cases(const std::vector<T> & values,ResultExpr result)367 Caser<T> Caser<T>::Cases(const std::vector<T>& values,
368                          ResultExpr result) const {
369   // Theoretically we could evaluate arg_ just once and emit a more efficient
370   // dispatch table, but for now we simply translate into an equivalent
371   // If/ElseIf/Else chain.
372 
373   typedef typename std::vector<T>::const_iterator Iter;
374   BoolExpr test = BoolConst(false);
375   for (Iter i = values.begin(), end = values.end(); i != end; ++i) {
376     test = test || (arg_ == *i);
377   }
378   return Caser<T>(arg_, elser_.ElseIf(test, result));
379 }
380 
381 template <typename T>
Default(ResultExpr result)382 ResultExpr Caser<T>::Default(ResultExpr result) const {
383   return elser_.Else(result);
384 }
385 
386 }  // namespace bpf_dsl
387 }  // namespace sandbox
388 
389 #endif  // SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
390