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 20namespace sandbox { 21class ErrorCode; 22class 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 78namespace sandbox { 79namespace bpf_dsl { 80 81// Forward declarations of classes; see below for proper documentation. 82class Elser; 83template <typename T> 84class Caser; 85namespace internal { 86class ResultExprImpl; 87class BoolExprImpl; 88} 89 90// ResultExpr is an opaque reference to an immutable result expression tree. 91typedef scoped_refptr<const internal::ResultExprImpl> ResultExpr; 92 93// BoolExpr is an opaque reference to an immutable boolean expression tree. 94typedef scoped_refptr<const internal::BoolExprImpl> BoolExpr; 95 96// Helper class to make writing policies easier. 97class SANDBOX_EXPORT SandboxBPFDSLPolicy : public SandboxBPFPolicy { 98 public: 99 SandboxBPFDSLPolicy() : SandboxBPFPolicy() {} 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. 125SANDBOX_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. 131SANDBOX_EXPORT ResultExpr Error(int err); 132 133// Kill specifies a result to kill the program and print an error message. 134SANDBOX_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. 139SANDBOX_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. 144SANDBOX_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. 154SANDBOX_EXPORT ResultExpr UnsafeTrap(Trap::TrapFnc trap_func, const void* aux); 155 156// BoolConst converts a bool value into a BoolExpr. 157SANDBOX_EXPORT BoolExpr BoolConst(bool value); 158 159// Various ways to combine boolean expressions into more complex expressions. 160// They follow standard boolean algebra laws. 161SANDBOX_EXPORT BoolExpr operator!(const BoolExpr& cond); 162SANDBOX_EXPORT BoolExpr operator&&(const BoolExpr& lhs, const BoolExpr& rhs); 163SANDBOX_EXPORT BoolExpr operator||(const BoolExpr& lhs, const BoolExpr& rhs); 164 165template <typename T> 166class 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 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: 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. 201SANDBOX_EXPORT Elser If(const BoolExpr& cond, const ResultExpr& then_result); 202 203class 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. 231template <typename T> 232SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg); 233 234template <typename T> 235class SANDBOX_EXPORT Caser { 236 public: 237 Caser(const Caser<T>& caser) : arg_(caser.arg_), elser_(caser.elser_) {} 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: 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. 287namespace 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. 293using bpf_dsl::operator!; 294using bpf_dsl::operator||; 295using 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. 301SANDBOX_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. 305SANDBOX_EXPORT uint64_t DefaultMask(size_t size); 306 307// Internal interface implemented by BoolExpr implementations. 308class SANDBOX_EXPORT BoolExprImpl : public base::RefCounted<BoolExprImpl> { 309 public: 310 BoolExprImpl() {} 311 virtual ErrorCode Compile(SandboxBPF* sb, 312 ErrorCode true_ec, 313 ErrorCode false_ec) const = 0; 314 315 protected: 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. 324class SANDBOX_EXPORT ResultExprImpl : public base::RefCounted<ResultExprImpl> { 325 public: 326 ResultExprImpl() {} 327 virtual ErrorCode Compile(SandboxBPF* sb) const = 0; 328 329 protected: 330 virtual ~ResultExprImpl() {} 331 332 private: 333 friend class base::RefCounted<ResultExprImpl>; 334 DISALLOW_COPY_AND_ASSIGN(ResultExprImpl); 335}; 336 337} // namespace internal 338 339template <typename T> 340Arg<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. 351template <typename T> 352BoolExpr Arg<T>::EqualTo(T val) const { 353 return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint64_t>(val)); 354} 355 356template <typename T> 357SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg) { 358 return Caser<T>(arg, Elser(Cons<Elser::Clause>::List())); 359} 360 361template <typename T> 362Caser<T> Caser<T>::Case(T value, ResultExpr result) const { 363 return SANDBOX_BPF_DSL_CASES((value), result); 364} 365 366template <typename T> 367Caser<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 381template <typename T> 382ResultExpr 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