1 // Copyright (c) 2012 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_SECCOMP_BPF_SANDBOX_BPF_H__ 6 #define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__ 7 8 #include <stddef.h> 9 #include <sys/types.h> 10 #include <sys/wait.h> 11 12 #include <algorithm> 13 #include <limits> 14 #include <map> 15 #include <set> 16 #include <utility> 17 #include <vector> 18 19 #include "base/memory/scoped_ptr.h" 20 #include "sandbox/linux/seccomp-bpf/die.h" 21 #include "sandbox/linux/seccomp-bpf/errorcode.h" 22 #include "sandbox/linux/seccomp-bpf/linux_seccomp.h" 23 24 namespace sandbox { 25 26 struct arch_seccomp_data { 27 int nr; 28 uint32_t arch; 29 uint64_t instruction_pointer; 30 uint64_t args[6]; 31 }; 32 33 struct arch_sigsys { 34 void* ip; 35 int nr; 36 unsigned int arch; 37 }; 38 39 class CodeGen; 40 class SandboxBPFPolicy; 41 class SandboxUnittestHelper; 42 struct Instruction; 43 44 class SandboxBPF { 45 public: 46 enum SandboxStatus { 47 STATUS_UNKNOWN, // Status prior to calling supportsSeccompSandbox() 48 STATUS_UNSUPPORTED, // The kernel does not appear to support sandboxing 49 STATUS_UNAVAILABLE, // Currently unavailable but might work again later 50 STATUS_AVAILABLE, // Sandboxing is available but not currently active 51 STATUS_ENABLED // The sandbox is now active 52 }; 53 54 // When calling setSandboxPolicy(), the caller can provide an arbitrary 55 // pointer in |aux|. This pointer will then be forwarded to the sandbox 56 // policy each time a call is made through an EvaluateSyscall function 57 // pointer. One common use case would be to pass the "aux" pointer as an 58 // argument to Trap() functions. 59 typedef ErrorCode (*EvaluateSyscall)(SandboxBPF* sandbox_compiler, 60 int system_call_number, 61 void* aux); 62 typedef std::vector<std::pair<EvaluateSyscall, void*> > Evaluators; 63 // A vector of BPF instructions that need to be installed as a filter 64 // program in the kernel. 65 typedef std::vector<struct sock_filter> Program; 66 67 // Constructors and destructors. 68 // NOTE: Setting a policy and starting the sandbox is a one-way operation. 69 // The kernel does not provide any option for unloading a loaded 70 // sandbox. Strictly speaking, that means we should disallow calling 71 // the destructor, if StartSandbox() has ever been called. In practice, 72 // this makes it needlessly complicated to operate on "Sandbox" 73 // objects. So, we instead opted to allow object destruction. But it 74 // should be noted that during its lifetime, the object probably made 75 // irreversible state changes to the runtime environment. These changes 76 // stay in effect even after the destructor has been run. 77 SandboxBPF(); 78 ~SandboxBPF(); 79 80 // Checks whether a particular system call number is valid on the current 81 // architecture. E.g. on ARM there's a non-contiguous range of private 82 // system calls. 83 static bool IsValidSyscallNumber(int sysnum); 84 85 // There are a lot of reasons why the Seccomp sandbox might not be available. 86 // This could be because the kernel does not support Seccomp mode, or it 87 // could be because another sandbox is already active. 88 // "proc_fd" should be a file descriptor for "/proc", or -1 if not 89 // provided by the caller. 90 static SandboxStatus SupportsSeccompSandbox(int proc_fd); 91 92 // The sandbox needs to be able to access files in "/proc/self". If this 93 // directory is not accessible when "startSandbox()" gets called, the caller 94 // can provide an already opened file descriptor by calling "set_proc_fd()". 95 // The sandbox becomes the new owner of this file descriptor and will 96 // eventually close it when "StartSandbox()" executes. 97 void set_proc_fd(int proc_fd); 98 99 // The system call evaluator function is called with the system 100 // call number. It can decide to allow the system call unconditionally 101 // by returning ERR_ALLOWED; it can deny the system call unconditionally by 102 // returning an appropriate "errno" value; or it can request inspection 103 // of system call argument(s) by returning a suitable ErrorCode. 104 // The "aux" parameter can be used to pass optional data to the system call 105 // evaluator. There are different possible uses for this data, but one of the 106 // use cases would be for the policy to then forward this pointer to a Trap() 107 // handler. In this case, of course, the data that is pointed to must remain 108 // valid for the entire time that Trap() handlers can be called; typically, 109 // this would be the lifetime of the program. 110 // DEPRECATED: use the policy interface below. 111 void SetSandboxPolicyDeprecated(EvaluateSyscall syscallEvaluator, void* aux); 112 113 // Set the BPF policy as |policy|. Ownership of |policy| is transfered here 114 // to the sandbox object. 115 void SetSandboxPolicy(SandboxBPFPolicy* policy); 116 117 // We can use ErrorCode to request calling of a trap handler. This method 118 // performs the required wrapping of the callback function into an 119 // ErrorCode object. 120 // The "aux" field can carry a pointer to arbitrary data. See EvaluateSyscall 121 // for a description of how to pass data from SetSandboxPolicy() to a Trap() 122 // handler. 123 ErrorCode Trap(Trap::TrapFnc fnc, const void* aux); 124 125 // Calls a user-space trap handler and disables all sandboxing for system 126 // calls made from this trap handler. 127 // This feature is available only if explicitly enabled by the user having 128 // set the CHROME_SANDBOX_DEBUGGING environment variable. 129 // Returns an ET_INVALID ErrorCode, if called when not enabled. 130 // NOTE: This feature, by definition, disables all security features of 131 // the sandbox. It should never be used in production, but it can be 132 // very useful to diagnose code that is incompatible with the sandbox. 133 // If even a single system call returns "UnsafeTrap", the security of 134 // entire sandbox should be considered compromised. 135 ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void* aux); 136 137 // From within an UnsafeTrap() it is often useful to be able to execute 138 // the system call that triggered the trap. The ForwardSyscall() method 139 // makes this easy. It is more efficient than calling glibc's syscall() 140 // function, as it avoid the extra round-trip to the signal handler. And 141 // it automatically does the correct thing to report kernel-style error 142 // conditions, rather than setting errno. See the comments for TrapFnc for 143 // details. In other words, the return value from ForwardSyscall() is 144 // directly suitable as a return value for a trap handler. 145 static intptr_t ForwardSyscall(const struct arch_seccomp_data& args); 146 147 // We can also use ErrorCode to request evaluation of a conditional 148 // statement based on inspection of system call parameters. 149 // This method wrap an ErrorCode object around the conditional statement. 150 // Argument "argno" (1..6) will be compared to "value" using comparator 151 // "op". If the condition is true "passed" will be returned, otherwise 152 // "failed". 153 // If "is32bit" is set, the argument must in the range of 0x0..(1u << 32 - 1) 154 // If it is outside this range, the sandbox treats the system call just 155 // the same as any other ABI violation (i.e. it aborts with an error 156 // message). 157 ErrorCode Cond(int argno, 158 ErrorCode::ArgType is_32bit, 159 ErrorCode::Operation op, 160 uint64_t value, 161 const ErrorCode& passed, 162 const ErrorCode& failed); 163 164 // Kill the program and print an error message. 165 ErrorCode Kill(const char* msg); 166 167 // This is the main public entry point. It finds all system calls that 168 // need rewriting, sets up the resources needed by the sandbox, and 169 // enters Seccomp mode. 170 // It is possible to stack multiple sandboxes by creating separate "Sandbox" 171 // objects and calling "StartSandbox()" on each of them. Please note, that 172 // this requires special care, though, as newly stacked sandboxes can never 173 // relax restrictions imposed by earlier sandboxes. Furthermore, installing 174 // a new policy requires making system calls, that might already be 175 // disallowed. 176 // Finally, stacking does add more kernel overhead than having a single 177 // combined policy. So, it should only be used if there are no alternatives. 178 void StartSandbox(); 179 180 // Assembles a BPF filter program from the current policy. After calling this 181 // function, you must not call any other sandboxing function. 182 // Typically, AssembleFilter() is only used by unit tests and by sandbox 183 // internals. It should not be used by production code. 184 // For performance reasons, we normally only run the assembled BPF program 185 // through the verifier, iff the program was built in debug mode. 186 // But by setting "force_verification", the caller can request that the 187 // verifier is run unconditionally. This is useful for unittests. 188 Program* AssembleFilter(bool force_verification); 189 190 // Returns the fatal ErrorCode that is used to indicate that somebody 191 // attempted to pass a 64bit value in a 32bit system call argument. 192 // This method is primarily needed for testing purposes. 193 ErrorCode Unexpected64bitArgument(); 194 195 private: 196 friend class CodeGen; 197 friend class SandboxUnittestHelper; 198 friend class ErrorCode; 199 200 struct Range { 201 Range(uint32_t f, uint32_t t, const ErrorCode& e) 202 : from(f), to(t), err(e) {} 203 uint32_t from, to; 204 ErrorCode err; 205 }; 206 typedef std::vector<Range> Ranges; 207 typedef std::map<uint32_t, ErrorCode> ErrMap; 208 typedef std::set<ErrorCode, struct ErrorCode::LessThan> Conds; 209 210 // Get a file descriptor pointing to "/proc", if currently available. 211 int proc_fd() { return proc_fd_; } 212 213 // Creates a subprocess and runs "code_in_sandbox" inside of the specified 214 // policy. The caller has to make sure that "this" has not yet been 215 // initialized with any other policies. 216 bool RunFunctionInPolicy(void (*code_in_sandbox)(), 217 EvaluateSyscall syscall_evaluator, 218 void* aux); 219 220 // Performs a couple of sanity checks to verify that the kernel supports the 221 // features that we need for successful sandboxing. 222 // The caller has to make sure that "this" has not yet been initialized with 223 // any other policies. 224 bool KernelSupportSeccompBPF(); 225 226 // Verify that the current policy passes some basic sanity checks. 227 void PolicySanityChecks(SandboxBPFPolicy* policy); 228 229 // Assembles and installs a filter based on the policy that has previously 230 // been configured with SetSandboxPolicy(). 231 void InstallFilter(); 232 233 // Verify the correctness of a compiled program by comparing it against the 234 // current policy. This function should only ever be called by unit tests and 235 // by the sandbox internals. It should not be used by production code. 236 void VerifyProgram(const Program& program, bool has_unsafe_traps); 237 238 // Finds all the ranges of system calls that need to be handled. Ranges are 239 // sorted in ascending order of system call numbers. There are no gaps in the 240 // ranges. System calls with identical ErrorCodes are coalesced into a single 241 // range. 242 void FindRanges(Ranges* ranges); 243 244 // Returns a BPF program snippet that implements a jump table for the 245 // given range of system call numbers. This function runs recursively. 246 Instruction* AssembleJumpTable(CodeGen* gen, 247 Ranges::const_iterator start, 248 Ranges::const_iterator stop); 249 250 // Returns a BPF program snippet that makes the BPF filter program exit 251 // with the given ErrorCode "err". N.B. the ErrorCode may very well be a 252 // conditional expression; if so, this function will recursively call 253 // CondExpression() and possibly RetExpression() to build a complex set of 254 // instructions. 255 Instruction* RetExpression(CodeGen* gen, const ErrorCode& err); 256 257 // Returns a BPF program that evaluates the conditional expression in 258 // "cond" and returns the appropriate value from the BPF filter program. 259 // This function recursively calls RetExpression(); it should only ever be 260 // called from RetExpression(). 261 Instruction* CondExpression(CodeGen* gen, const ErrorCode& cond); 262 263 static SandboxStatus status_; 264 265 bool quiet_; 266 int proc_fd_; 267 scoped_ptr<const SandboxBPFPolicy> policy_; 268 Conds* conds_; 269 bool sandbox_has_started_; 270 271 DISALLOW_COPY_AND_ASSIGN(SandboxBPF); 272 }; 273 274 } // namespace sandbox 275 276 #endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__ 277