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.
5 #ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
6 #define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
14 #include "base/compiler_specific.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "sandbox/linux/seccomp-bpf/errorcode.h"
17 #include "sandbox/linux/seccomp-bpf/trap.h"
18 #include "sandbox/sandbox_export.h"
24 class SandboxBPFPolicy;
25 class SandboxUnittestHelper;
28 class SANDBOX_EXPORT SandboxBPF {
31 STATUS_UNKNOWN, // Status prior to calling supportsSeccompSandbox()
32 STATUS_UNSUPPORTED, // The kernel does not appear to support sandboxing
33 STATUS_UNAVAILABLE, // Currently unavailable but might work again later
34 STATUS_AVAILABLE, // Sandboxing is available but not currently active
35 STATUS_ENABLED // The sandbox is now active
38 // Depending on the level of kernel support, seccomp-bpf may require the
39 // process to be single-threaded in order to enable it. When calling
40 // StartSandbox(), the program should indicate whether or not the sandbox
41 // should try and engage with multi-thread support.
42 enum SandboxThreadState {
44 PROCESS_SINGLE_THREADED, // The program is currently single-threaded.
45 // Note: PROCESS_MULTI_THREADED requires experimental kernel support that
46 // has not been contributed to upstream Linux.
47 PROCESS_MULTI_THREADED, // The program may be multi-threaded.
50 // A vector of BPF instructions that need to be installed as a filter
51 // program in the kernel.
52 typedef std::vector<struct sock_filter> Program;
54 // Constructors and destructors.
55 // NOTE: Setting a policy and starting the sandbox is a one-way operation.
56 // The kernel does not provide any option for unloading a loaded
57 // sandbox. Strictly speaking, that means we should disallow calling
58 // the destructor, if StartSandbox() has ever been called. In practice,
59 // this makes it needlessly complicated to operate on "Sandbox"
60 // objects. So, we instead opted to allow object destruction. But it
61 // should be noted that during its lifetime, the object probably made
62 // irreversible state changes to the runtime environment. These changes
63 // stay in effect even after the destructor has been run.
67 // Checks whether a particular system call number is valid on the current
68 // architecture. E.g. on ARM there's a non-contiguous range of private
70 static bool IsValidSyscallNumber(int sysnum);
72 // There are a lot of reasons why the Seccomp sandbox might not be available.
73 // This could be because the kernel does not support Seccomp mode, or it
74 // could be because another sandbox is already active.
75 // "proc_fd" should be a file descriptor for "/proc", or -1 if not
76 // provided by the caller.
77 static SandboxStatus SupportsSeccompSandbox(int proc_fd);
79 // Determines if the kernel has support for the seccomp() system call to
80 // synchronize BPF filters across a thread group.
81 static SandboxStatus SupportsSeccompThreadFilterSynchronization();
83 // The sandbox needs to be able to access files in "/proc/self". If this
84 // directory is not accessible when "startSandbox()" gets called, the caller
85 // can provide an already opened file descriptor by calling "set_proc_fd()".
86 // The sandbox becomes the new owner of this file descriptor and will
87 // eventually close it when "StartSandbox()" executes.
88 void set_proc_fd(int proc_fd);
90 // Set the BPF policy as |policy|. Ownership of |policy| is transfered here
91 // to the sandbox object.
92 void SetSandboxPolicy(SandboxBPFPolicy* policy);
94 // We can use ErrorCode to request calling of a trap handler. This method
95 // performs the required wrapping of the callback function into an
97 // The "aux" field can carry a pointer to arbitrary data. See EvaluateSyscall
98 // for a description of how to pass data from SetSandboxPolicy() to a Trap()
100 static ErrorCode Trap(Trap::TrapFnc fnc, const void* aux);
102 // Calls a user-space trap handler and disables all sandboxing for system
103 // calls made from this trap handler.
104 // This feature is available only if explicitly enabled by the user having
105 // set the CHROME_SANDBOX_DEBUGGING environment variable.
106 // Returns an ET_INVALID ErrorCode, if called when not enabled.
107 // NOTE: This feature, by definition, disables all security features of
108 // the sandbox. It should never be used in production, but it can be
109 // very useful to diagnose code that is incompatible with the sandbox.
110 // If even a single system call returns "UnsafeTrap", the security of
111 // entire sandbox should be considered compromised.
112 static ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void* aux);
114 // UnsafeTraps require some syscalls to always be allowed.
115 // This helper function returns true for these calls.
116 static bool IsRequiredForUnsafeTrap(int sysno);
118 // From within an UnsafeTrap() it is often useful to be able to execute
119 // the system call that triggered the trap. The ForwardSyscall() method
120 // makes this easy. It is more efficient than calling glibc's syscall()
121 // function, as it avoid the extra round-trip to the signal handler. And
122 // it automatically does the correct thing to report kernel-style error
123 // conditions, rather than setting errno. See the comments for TrapFnc for
124 // details. In other words, the return value from ForwardSyscall() is
125 // directly suitable as a return value for a trap handler.
126 static intptr_t ForwardSyscall(const struct arch_seccomp_data& args);
128 // We can also use ErrorCode to request evaluation of a conditional
129 // statement based on inspection of system call parameters.
130 // This method wrap an ErrorCode object around the conditional statement.
131 // Argument "argno" (1..6) will be bitwise-AND'd with "mask" and compared
132 // to "value"; if equal, then "passed" will be returned, otherwise "failed".
133 // If "is32bit" is set, the argument must in the range of 0x0..(1u << 32 - 1)
134 // If it is outside this range, the sandbox treats the system call just
135 // the same as any other ABI violation (i.e. it aborts with an error
137 ErrorCode CondMaskedEqual(int argno,
138 ErrorCode::ArgType is_32bit,
141 const ErrorCode& passed,
142 const ErrorCode& failed);
144 // Legacy variant of CondMaskedEqual that supports a few comparison
145 // operations, which get converted into masked-equality comparisons.
146 ErrorCode Cond(int argno,
147 ErrorCode::ArgType is_32bit,
148 ErrorCode::Operation op,
150 const ErrorCode& passed,
151 const ErrorCode& failed);
153 // Kill the program and print an error message.
154 static ErrorCode Kill(const char* msg);
156 // This is the main public entry point. It finds all system calls that
157 // need rewriting, sets up the resources needed by the sandbox, and
158 // enters Seccomp mode.
159 // The calling process must specify its current SandboxThreadState, as a way
160 // to tell the sandbox which type of kernel support it should engage.
161 // It is possible to stack multiple sandboxes by creating separate "Sandbox"
162 // objects and calling "StartSandbox()" on each of them. Please note, that
163 // this requires special care, though, as newly stacked sandboxes can never
164 // relax restrictions imposed by earlier sandboxes. Furthermore, installing
165 // a new policy requires making system calls, that might already be
167 // Finally, stacking does add more kernel overhead than having a single
168 // combined policy. So, it should only be used if there are no alternatives.
169 bool StartSandbox(SandboxThreadState thread_state) WARN_UNUSED_RESULT;
171 // Assembles a BPF filter program from the current policy. After calling this
172 // function, you must not call any other sandboxing function.
173 // Typically, AssembleFilter() is only used by unit tests and by sandbox
174 // internals. It should not be used by production code.
175 // For performance reasons, we normally only run the assembled BPF program
176 // through the verifier, iff the program was built in debug mode.
177 // But by setting "force_verification", the caller can request that the
178 // verifier is run unconditionally. This is useful for unittests.
179 Program* AssembleFilter(bool force_verification);
181 // Returns the fatal ErrorCode that is used to indicate that somebody
182 // attempted to pass a 64bit value in a 32bit system call argument.
183 // This method is primarily needed for testing purposes.
184 static ErrorCode Unexpected64bitArgument();
187 friend class CodeGen;
188 friend class SandboxUnittestHelper;
189 friend class ErrorCode;
192 Range(uint32_t f, uint32_t t, const ErrorCode& e)
193 : from(f), to(t), err(e) {}
197 typedef std::vector<Range> Ranges;
198 typedef std::map<uint32_t, ErrorCode> ErrMap;
199 typedef std::set<ErrorCode, struct ErrorCode::LessThan> Conds;
201 // Used by CondExpressionHalf to track which half of the argument it's
202 // emitting instructions for.
208 // Get a file descriptor pointing to "/proc", if currently available.
209 int proc_fd() { return proc_fd_; }
211 // Creates a subprocess and runs "code_in_sandbox" inside of the specified
212 // policy. The caller has to make sure that "this" has not yet been
213 // initialized with any other policies.
214 bool RunFunctionInPolicy(void (*code_in_sandbox)(),
215 scoped_ptr<SandboxBPFPolicy> policy);
217 // Performs a couple of sanity checks to verify that the kernel supports the
218 // features that we need for successful sandboxing.
219 // The caller has to make sure that "this" has not yet been initialized with
220 // any other policies.
221 bool KernelSupportSeccompBPF();
223 // Verify that the current policy passes some basic sanity checks.
224 void PolicySanityChecks(SandboxBPFPolicy* policy);
226 // Assembles and installs a filter based on the policy that has previously
227 // been configured with SetSandboxPolicy().
228 void InstallFilter(bool must_sync_threads);
230 // Compile the configured policy into a complete instruction sequence.
231 // (See MaybeAddEscapeHatch for |has_unsafe_traps|.)
232 Instruction* CompilePolicy(CodeGen* gen, bool* has_unsafe_traps);
234 // Return an instruction sequence that checks the
235 // arch_seccomp_data's "arch" field is valid, and then passes
236 // control to |passed| if so.
237 Instruction* CheckArch(CodeGen* gen, Instruction* passed);
239 // If the |rest| instruction sequence contains any unsafe traps,
240 // then sets |*has_unsafe_traps| to true and returns an instruction
241 // sequence that allows all system calls from Syscall::Call(), and
242 // otherwise passes control to |rest|.
244 // If |rest| contains no unsafe traps, then |rest| is returned
245 // directly and |*has_unsafe_traps| is set to false.
246 Instruction* MaybeAddEscapeHatch(CodeGen* gen,
247 bool* has_unsafe_traps,
250 // Return an instruction sequence that loads and checks the system
251 // call number, performs a binary search, and then dispatches to an
252 // appropriate instruction sequence compiled from the current
254 Instruction* DispatchSyscall(CodeGen* gen);
256 // Return an instruction sequence that checks the system call number
257 // (expected to be loaded in register A) and if valid, passes
258 // control to |passed| (with register A still valid).
259 Instruction* CheckSyscallNumber(CodeGen* gen, Instruction* passed);
261 // Verify the correctness of a compiled program by comparing it against the
262 // current policy. This function should only ever be called by unit tests and
263 // by the sandbox internals. It should not be used by production code.
264 void VerifyProgram(const Program& program, bool has_unsafe_traps);
266 // Finds all the ranges of system calls that need to be handled. Ranges are
267 // sorted in ascending order of system call numbers. There are no gaps in the
268 // ranges. System calls with identical ErrorCodes are coalesced into a single
270 void FindRanges(Ranges* ranges);
272 // Returns a BPF program snippet that implements a jump table for the
273 // given range of system call numbers. This function runs recursively.
274 Instruction* AssembleJumpTable(CodeGen* gen,
275 Ranges::const_iterator start,
276 Ranges::const_iterator stop);
278 // Returns a BPF program snippet that makes the BPF filter program exit
279 // with the given ErrorCode "err". N.B. the ErrorCode may very well be a
280 // conditional expression; if so, this function will recursively call
281 // CondExpression() and possibly RetExpression() to build a complex set of
283 Instruction* RetExpression(CodeGen* gen, const ErrorCode& err);
285 // Returns a BPF program that evaluates the conditional expression in
286 // "cond" and returns the appropriate value from the BPF filter program.
287 // This function recursively calls RetExpression(); it should only ever be
288 // called from RetExpression().
289 Instruction* CondExpression(CodeGen* gen, const ErrorCode& cond);
291 // Returns a BPF program that evaluates half of a conditional expression;
292 // it should only ever be called from CondExpression().
293 Instruction* CondExpressionHalf(CodeGen* gen,
294 const ErrorCode& cond,
297 Instruction* failed);
299 static SandboxStatus status_;
303 scoped_ptr<const SandboxBPFPolicy> policy_;
305 bool sandbox_has_started_;
307 DISALLOW_COPY_AND_ASSIGN(SandboxBPF);
310 } // namespace sandbox
312 #endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__