sandbox_bpf.h revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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
24namespace sandbox {
25
26struct arch_seccomp_data {
27  int nr;
28  uint32_t arch;
29  uint64_t instruction_pointer;
30  uint64_t args[6];
31};
32
33struct arch_sigsys {
34  void* ip;
35  int nr;
36  unsigned int arch;
37};
38
39class CodeGen;
40class SandboxBPFPolicy;
41class SandboxUnittestHelper;
42struct Instruction;
43
44class 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