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: 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. 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 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. 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: 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. 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: 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. 324 class 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 339 template <typename T> 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> 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> 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> 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> 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> 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