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