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.
5 #ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
6 #define SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
13 #include "base/macros.h"
14 #include "base/memory/ref_counted.h"
15 #include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
16 #include "sandbox/linux/bpf_dsl/cons.h"
17 #include "sandbox/linux/bpf_dsl/trap_registry.h"
18 #include "sandbox/sandbox_export.h"
20 // The sandbox::bpf_dsl namespace provides a domain-specific language
21 // to make writing BPF policies more expressive. In general, the
22 // object types all have value semantics (i.e., they can be copied
23 // around, returned from or passed to function calls, etc. without any
24 // surprising side effects), though not all support assignment.
26 // An idiomatic and demonstrative (albeit silly) example of this API
29 // #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
31 // using namespace sandbox::bpf_dsl;
33 // class SillyPolicy : public Policy {
36 // ~SillyPolicy() override {}
37 // ResultExpr EvaluateSyscall(int sysno) const override {
38 // if (sysno == __NR_fcntl) {
39 // Arg<int> fd(0), cmd(1);
40 // Arg<unsigned long> flags(2);
41 // const uint64_t kGoodFlags = O_ACCMODE | O_NONBLOCK;
42 // return If(fd == 0 && cmd == F_SETFL && (flags & ~kGoodFlags) == 0,
44 // .ElseIf(cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC,
46 // .Else(Trap(SetFlagHandler, NULL));
53 // DISALLOW_COPY_AND_ASSIGN(SillyPolicy);
56 // More generally, the DSL currently supports the following grammar:
58 // result = Allow() | Error(errno) | Kill() | Trace(aux)
59 // | Trap(trap_func, aux) | UnsafeTrap(trap_func, aux)
60 // | If(bool, result)[.ElseIf(bool, result)].Else(result)
61 // | Switch(arg)[.Case(val, result)].Default(result)
62 // bool = BoolConst(boolean) | !bool | bool && bool | bool || bool
63 // | arg == val | arg != val
64 // arg = Arg<T>(num) | arg & mask
66 // The semantics of each function and operator are intended to be
67 // intuitive, but are described in more detail below.
69 // (Credit to Sean Parent's "Inheritance is the Base Class of Evil"
70 // talk at Going Native 2013 for promoting value semantics via shared
71 // pointers to immutable state.)
76 // ResultExpr is an opaque reference to an immutable result expression tree.
77 typedef scoped_refptr
<const internal::ResultExprImpl
> ResultExpr
;
79 // BoolExpr is an opaque reference to an immutable boolean expression tree.
80 typedef scoped_refptr
<const internal::BoolExprImpl
> BoolExpr
;
82 // Allow specifies a result that the system call should be allowed to
84 SANDBOX_EXPORT ResultExpr
Allow();
86 // Error specifies a result that the system call should fail with
87 // error number |err|. As a special case, Error(0) will result in the
88 // system call appearing to have succeeded, but without having any
90 SANDBOX_EXPORT ResultExpr
Error(int err
);
92 // Kill specifies a result to kill the process (task) immediately.
93 SANDBOX_EXPORT ResultExpr
Kill();
95 // Trace specifies a result to notify a tracing process via the
96 // PTRACE_EVENT_SECCOMP event and allow it to change or skip the system call.
97 // The value of |aux| will be available to the tracer via PTRACE_GETEVENTMSG.
98 SANDBOX_EXPORT ResultExpr
Trace(uint16_t aux
);
100 // Trap specifies a result that the system call should be handled by
101 // trapping back into userspace and invoking |trap_func|, passing
102 // |aux| as the second parameter.
103 SANDBOX_EXPORT ResultExpr
104 Trap(TrapRegistry::TrapFnc trap_func
, const void* aux
);
106 // UnsafeTrap is like Trap, except the policy is marked as "unsafe"
107 // and allowed to use SandboxSyscall to invoke any system call.
109 // NOTE: This feature, by definition, disables all security features of
110 // the sandbox. It should never be used in production, but it can be
111 // very useful to diagnose code that is incompatible with the sandbox.
112 // If even a single system call returns "UnsafeTrap", the security of
113 // entire sandbox should be considered compromised.
114 SANDBOX_EXPORT ResultExpr
115 UnsafeTrap(TrapRegistry::TrapFnc trap_func
, const void* aux
);
117 // BoolConst converts a bool value into a BoolExpr.
118 SANDBOX_EXPORT BoolExpr
BoolConst(bool value
);
120 // Various ways to combine boolean expressions into more complex expressions.
121 // They follow standard boolean algebra laws.
122 SANDBOX_EXPORT BoolExpr
operator!(const BoolExpr
& cond
);
123 SANDBOX_EXPORT BoolExpr
operator&&(const BoolExpr
& lhs
, const BoolExpr
& rhs
);
124 SANDBOX_EXPORT BoolExpr
operator||(const BoolExpr
& lhs
, const BoolExpr
& rhs
);
126 template <typename T
>
127 class SANDBOX_EXPORT Arg
{
129 // Initializes the Arg to represent the |num|th system call
130 // argument (indexed from 0), which is of type |T|.
131 explicit Arg(int num
);
133 Arg(const Arg
& arg
) : num_(arg
.num_
), mask_(arg
.mask_
) {}
135 // Returns an Arg representing the current argument, but after
136 // bitwise-and'ing it with |rhs|.
137 friend Arg
operator&(const Arg
& lhs
, uint64_t rhs
) {
138 return Arg(lhs
.num_
, lhs
.mask_
& rhs
);
141 // Returns a boolean expression comparing whether the system call argument
142 // (after applying any bitmasks, if appropriate) equals |rhs|.
143 friend BoolExpr
operator==(const Arg
& lhs
, T rhs
) { return lhs
.EqualTo(rhs
); }
145 // Returns a boolean expression comparing whether the system call argument
146 // (after applying any bitmasks, if appropriate) does not equal |rhs|.
147 friend BoolExpr
operator!=(const Arg
& lhs
, T rhs
) { return !(lhs
== rhs
); }
150 Arg(int num
, uint64_t mask
) : num_(num
), mask_(mask
) {}
152 BoolExpr
EqualTo(T val
) const;
157 DISALLOW_ASSIGN(Arg
);
160 // If begins a conditional result expression predicated on the
161 // specified boolean expression.
162 SANDBOX_EXPORT Elser
If(const BoolExpr
& cond
, const ResultExpr
& then_result
);
164 class SANDBOX_EXPORT Elser
{
166 Elser(const Elser
& elser
);
169 // ElseIf extends the conditional result expression with another
170 // "if then" clause, predicated on the specified boolean expression.
171 Elser
ElseIf(const BoolExpr
& cond
, const ResultExpr
& then_result
) const;
173 // Else terminates a conditional result expression using |else_result| as
174 // the default fallback result expression.
175 ResultExpr
Else(const ResultExpr
& else_result
) const;
178 typedef std::pair
<BoolExpr
, ResultExpr
> Clause
;
180 explicit Elser(cons::List
<Clause
> clause_list
);
182 cons::List
<Clause
> clause_list_
;
184 friend Elser
If(const BoolExpr
&, const ResultExpr
&);
185 template <typename T
>
186 friend Caser
<T
> Switch(const Arg
<T
>&);
187 DISALLOW_ASSIGN(Elser
);
190 // Switch begins a switch expression dispatched according to the
191 // specified argument value.
192 template <typename T
>
193 SANDBOX_EXPORT Caser
<T
> Switch(const Arg
<T
>& arg
);
195 template <typename T
>
196 class SANDBOX_EXPORT Caser
{
198 Caser(const Caser
<T
>& caser
) : arg_(caser
.arg_
), elser_(caser
.elser_
) {}
201 // Case adds a single-value "case" clause to the switch.
202 Caser
<T
> Case(T value
, ResultExpr result
) const;
204 // Cases adds a multiple-value "case" clause to the switch.
205 // See also the SANDBOX_BPF_DSL_CASES macro below for a more idiomatic way
206 // of using this function.
207 Caser
<T
> Cases(const std::vector
<T
>& values
, ResultExpr result
) const;
209 // Terminate the switch with a "default" clause.
210 ResultExpr
Default(ResultExpr result
) const;
213 Caser(const Arg
<T
>& arg
, Elser elser
) : arg_(arg
), elser_(elser
) {}
218 template <typename U
>
219 friend Caser
<U
> Switch(const Arg
<U
>&);
220 DISALLOW_ASSIGN(Caser
);
223 // Recommended usage is to put
224 // #define CASES SANDBOX_BPF_DSL_CASES
225 // near the top of the .cc file (e.g., nearby any "using" statements), then
227 // Switch(arg).CASES((3, 5, 7), result)...;
228 #define SANDBOX_BPF_DSL_CASES(values, result) \
229 Cases(SANDBOX_BPF_DSL_CASES_HELPER values, result)
231 // Helper macro to construct a std::vector from an initializer list.
232 // TODO(mdempsky): Convert to use C++11 initializer lists instead.
233 #define SANDBOX_BPF_DSL_CASES_HELPER(value, ...) \
235 const __typeof__(value) bpf_dsl_cases_values[] = {value, __VA_ARGS__}; \
236 std::vector<__typeof__(value)>( \
237 bpf_dsl_cases_values, \
238 bpf_dsl_cases_values + arraysize(bpf_dsl_cases_values)); \
241 // =====================================================================
242 // Official API ends here.
243 // =====================================================================
247 // Make argument-dependent lookup work. This is necessary because although
248 // BoolExpr is defined in bpf_dsl, since it's merely a typedef for
249 // scoped_refptr<const internal::BoolExplImpl>, argument-dependent lookup only
250 // searches the "internal" nested namespace.
251 using bpf_dsl::operator!;
252 using bpf_dsl::operator||;
253 using bpf_dsl::operator&&;
255 // Returns a boolean expression that represents whether system call
256 // argument |num| of size |size| is equal to |val|, when masked
257 // according to |mask|. Users should use the Arg template class below
258 // instead of using this API directly.
259 SANDBOX_EXPORT BoolExpr
260 ArgEq(int num
, size_t size
, uint64_t mask
, uint64_t val
);
262 // Returns the default mask for a system call argument of the specified size.
263 SANDBOX_EXPORT
uint64_t DefaultMask(size_t size
);
265 } // namespace internal
267 template <typename T
>
269 : num_(num
), mask_(internal::DefaultMask(sizeof(T
))) {
272 // Definition requires ArgEq to have been declared. Moved out-of-line
273 // to minimize how much internal clutter users have to ignore while
274 // reading the header documentation.
276 // Additionally, we use this helper member function to avoid linker errors
277 // caused by defining operator== out-of-line. For a more detailed explanation,
278 // see http://www.parashift.com/c++-faq-lite/template-friends.html.
279 template <typename T
>
280 BoolExpr Arg
<T
>::EqualTo(T val
) const {
281 if (sizeof(T
) == 4) {
282 // Prevent sign-extension of negative int32_t values.
283 return internal::ArgEq(num_
, sizeof(T
), mask_
, static_cast<uint32_t>(val
));
285 return internal::ArgEq(num_
, sizeof(T
), mask_
, static_cast<uint64_t>(val
));
288 template <typename T
>
289 SANDBOX_EXPORT Caser
<T
> Switch(const Arg
<T
>& arg
) {
290 return Caser
<T
>(arg
, Elser(nullptr));
293 template <typename T
>
294 Caser
<T
> Caser
<T
>::Case(T value
, ResultExpr result
) const {
295 return SANDBOX_BPF_DSL_CASES((value
), result
);
298 template <typename T
>
299 Caser
<T
> Caser
<T
>::Cases(const std::vector
<T
>& values
,
300 ResultExpr result
) const {
301 // Theoretically we could evaluate arg_ just once and emit a more efficient
302 // dispatch table, but for now we simply translate into an equivalent
303 // If/ElseIf/Else chain.
305 typedef typename
std::vector
<T
>::const_iterator Iter
;
306 BoolExpr test
= BoolConst(false);
307 for (Iter i
= values
.begin(), end
= values
.end(); i
!= end
; ++i
) {
308 test
= test
|| (arg_
== *i
);
310 return Caser
<T
>(arg_
, elser_
.ElseIf(test
, result
));
313 template <typename T
>
314 ResultExpr Caser
<T
>::Default(ResultExpr result
) const {
315 return elser_
.Else(result
);
318 } // namespace bpf_dsl
319 } // namespace sandbox
321 #endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_