#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
-#include <stddef.h>
-#include <sys/types.h>
-#include <sys/wait.h>
+#include <stdint.h>
-#include <algorithm>
-#include <limits>
#include <map>
#include <set>
-#include <utility>
#include <vector>
-#include "sandbox/linux/seccomp-bpf/die.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
#include "sandbox/linux/seccomp-bpf/errorcode.h"
-#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
-#include "sandbox/linux/seccomp-bpf/port.h"
-#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/sandbox_export.h"
-namespace playground2 {
-
-struct arch_seccomp_data {
- int nr;
- uint32_t arch;
- uint64_t instruction_pointer;
- uint64_t args[6];
-};
-
-struct arch_sigsys {
- void *ip;
- int nr;
- unsigned int arch;
-};
+struct sock_filter;
+namespace sandbox {
class CodeGen;
+class SandboxBPFPolicy;
class SandboxUnittestHelper;
struct Instruction;
-class Sandbox {
+class SANDBOX_EXPORT SandboxBPF {
public:
enum SandboxStatus {
STATUS_UNKNOWN, // Status prior to calling supportsSeccompSandbox()
STATUS_ENABLED // The sandbox is now active
};
- // BpfSandboxPolicy is the following type:
- // ErrorCode (Sandbox *sb, int sysnum, void *aux);
- // When calling setSandboxPolicy(), the caller can provide an arbitrary
- // pointer in |aux|. This pointer will then be forwarded to the sandbox
- // policy each time a call is made through an EvaluateSyscall function
- // pointer. One common use case would be to pass the "aux" pointer as an
- // argument to Trap() functions.
- typedef BpfSandboxPolicy* EvaluateSyscall;
- typedef std::vector<std::pair<EvaluateSyscall, void *> >Evaluators;
+ // Depending on the level of kernel support, seccomp-bpf may require the
+ // process to be single-threaded in order to enable it. When calling
+ // StartSandbox(), the program should indicate whether or not the sandbox
+ // should try and engage with multi-thread support.
+ enum SandboxThreadState {
+ PROCESS_INVALID,
+ PROCESS_SINGLE_THREADED, // The program is currently single-threaded.
+ // Note: PROCESS_MULTI_THREADED requires experimental kernel support that
+ // has not been contributed to upstream Linux.
+ PROCESS_MULTI_THREADED, // The program may be multi-threaded.
+ };
// A vector of BPF instructions that need to be installed as a filter
// program in the kernel.
// should be noted that during its lifetime, the object probably made
// irreversible state changes to the runtime environment. These changes
// stay in effect even after the destructor has been run.
- Sandbox();
- ~Sandbox();
+ SandboxBPF();
+ ~SandboxBPF();
// Checks whether a particular system call number is valid on the current
// architecture. E.g. on ARM there's a non-contiguous range of private
// provided by the caller.
static SandboxStatus SupportsSeccompSandbox(int proc_fd);
+ // Determines if the kernel has support for the seccomp() system call to
+ // synchronize BPF filters across a thread group.
+ static SandboxStatus SupportsSeccompThreadFilterSynchronization();
+
// The sandbox needs to be able to access files in "/proc/self". If this
// directory is not accessible when "startSandbox()" gets called, the caller
// can provide an already opened file descriptor by calling "set_proc_fd()".
// eventually close it when "StartSandbox()" executes.
void set_proc_fd(int proc_fd);
- // The system call evaluator function is called with the system
- // call number. It can decide to allow the system call unconditionally
- // by returning ERR_ALLOWED; it can deny the system call unconditionally by
- // returning an appropriate "errno" value; or it can request inspection
- // of system call argument(s) by returning a suitable ErrorCode.
- // The "aux" parameter can be used to pass optional data to the system call
- // evaluator. There are different possible uses for this data, but one of the
- // use cases would be for the policy to then forward this pointer to a Trap()
- // handler. In this case, of course, the data that is pointed to must remain
- // valid for the entire time that Trap() handlers can be called; typically,
- // this would be the lifetime of the program.
- void SetSandboxPolicy(EvaluateSyscall syscallEvaluator, void *aux);
+ // Set the BPF policy as |policy|. Ownership of |policy| is transfered here
+ // to the sandbox object.
+ void SetSandboxPolicy(SandboxBPFPolicy* policy);
// We can use ErrorCode to request calling of a trap handler. This method
// performs the required wrapping of the callback function into an
// The "aux" field can carry a pointer to arbitrary data. See EvaluateSyscall
// for a description of how to pass data from SetSandboxPolicy() to a Trap()
// handler.
- ErrorCode Trap(Trap::TrapFnc fnc, const void *aux);
+ static ErrorCode Trap(Trap::TrapFnc fnc, const void* aux);
// Calls a user-space trap handler and disables all sandboxing for system
// calls made from this trap handler.
// very useful to diagnose code that is incompatible with the sandbox.
// If even a single system call returns "UnsafeTrap", the security of
// entire sandbox should be considered compromised.
- ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void *aux);
+ static ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void* aux);
+
+ // UnsafeTraps require some syscalls to always be allowed.
+ // This helper function returns true for these calls.
+ static bool IsRequiredForUnsafeTrap(int sysno);
// From within an UnsafeTrap() it is often useful to be able to execute
// the system call that triggered the trap. The ForwardSyscall() method
// We can also use ErrorCode to request evaluation of a conditional
// statement based on inspection of system call parameters.
// This method wrap an ErrorCode object around the conditional statement.
- // Argument "argno" (1..6) will be compared to "value" using comparator
- // "op". If the condition is true "passed" will be returned, otherwise
- // "failed".
+ // Argument "argno" (1..6) will be bitwise-AND'd with "mask" and compared
+ // to "value"; if equal, then "passed" will be returned, otherwise "failed".
// If "is32bit" is set, the argument must in the range of 0x0..(1u << 32 - 1)
// If it is outside this range, the sandbox treats the system call just
// the same as any other ABI violation (i.e. it aborts with an error
// message).
- ErrorCode Cond(int argno, ErrorCode::ArgType is_32bit,
+ ErrorCode CondMaskedEqual(int argno,
+ ErrorCode::ArgType is_32bit,
+ uint64_t mask,
+ uint64_t value,
+ const ErrorCode& passed,
+ const ErrorCode& failed);
+
+ // Legacy variant of CondMaskedEqual that supports a few comparison
+ // operations, which get converted into masked-equality comparisons.
+ ErrorCode Cond(int argno,
+ ErrorCode::ArgType is_32bit,
ErrorCode::Operation op,
- uint64_t value, const ErrorCode& passed,
+ uint64_t value,
+ const ErrorCode& passed,
const ErrorCode& failed);
// Kill the program and print an error message.
- ErrorCode Kill(const char *msg);
+ static ErrorCode Kill(const char* msg);
// This is the main public entry point. It finds all system calls that
// need rewriting, sets up the resources needed by the sandbox, and
// enters Seccomp mode.
+ // The calling process must specify its current SandboxThreadState, as a way
+ // to tell the sandbox which type of kernel support it should engage.
// It is possible to stack multiple sandboxes by creating separate "Sandbox"
// objects and calling "StartSandbox()" on each of them. Please note, that
// this requires special care, though, as newly stacked sandboxes can never
// disallowed.
// Finally, stacking does add more kernel overhead than having a single
// combined policy. So, it should only be used if there are no alternatives.
- void StartSandbox();
+ bool StartSandbox(SandboxThreadState thread_state) WARN_UNUSED_RESULT;
// Assembles a BPF filter program from the current policy. After calling this
// function, you must not call any other sandboxing function.
// through the verifier, iff the program was built in debug mode.
// But by setting "force_verification", the caller can request that the
// verifier is run unconditionally. This is useful for unittests.
- Program *AssembleFilter(bool force_verification);
+ Program* AssembleFilter(bool force_verification);
// Returns the fatal ErrorCode that is used to indicate that somebody
// attempted to pass a 64bit value in a 32bit system call argument.
// This method is primarily needed for testing purposes.
- ErrorCode Unexpected64bitArgument();
+ static ErrorCode Unexpected64bitArgument();
private:
friend class CodeGen;
struct Range {
Range(uint32_t f, uint32_t t, const ErrorCode& e)
- : from(f),
- to(t),
- err(e) {
- }
- uint32_t from, to;
+ : from(f), to(t), err(e) {}
+ uint32_t from, to;
ErrorCode err;
};
typedef std::vector<Range> Ranges;
typedef std::map<uint32_t, ErrorCode> ErrMap;
typedef std::set<ErrorCode, struct ErrorCode::LessThan> Conds;
+ // Used by CondExpressionHalf to track which half of the argument it's
+ // emitting instructions for.
+ enum ArgHalf {
+ LowerHalf,
+ UpperHalf,
+ };
+
// Get a file descriptor pointing to "/proc", if currently available.
int proc_fd() { return proc_fd_; }
// policy. The caller has to make sure that "this" has not yet been
// initialized with any other policies.
bool RunFunctionInPolicy(void (*code_in_sandbox)(),
- EvaluateSyscall syscall_evaluator, void *aux);
+ scoped_ptr<SandboxBPFPolicy> policy);
// Performs a couple of sanity checks to verify that the kernel supports the
// features that we need for successful sandboxing.
bool KernelSupportSeccompBPF();
// Verify that the current policy passes some basic sanity checks.
- void PolicySanityChecks(EvaluateSyscall syscall_evaluator, void *aux);
+ void PolicySanityChecks(SandboxBPFPolicy* policy);
// Assembles and installs a filter based on the policy that has previously
// been configured with SetSandboxPolicy().
- void InstallFilter();
+ void InstallFilter(bool must_sync_threads);
+
+ // Compile the configured policy into a complete instruction sequence.
+ // (See MaybeAddEscapeHatch for |has_unsafe_traps|.)
+ Instruction* CompilePolicy(CodeGen* gen, bool* has_unsafe_traps);
+
+ // Return an instruction sequence that checks the
+ // arch_seccomp_data's "arch" field is valid, and then passes
+ // control to |passed| if so.
+ Instruction* CheckArch(CodeGen* gen, Instruction* passed);
+
+ // If the |rest| instruction sequence contains any unsafe traps,
+ // then sets |*has_unsafe_traps| to true and returns an instruction
+ // sequence that allows all system calls from Syscall::Call(), and
+ // otherwise passes control to |rest|.
+ //
+ // If |rest| contains no unsafe traps, then |rest| is returned
+ // directly and |*has_unsafe_traps| is set to false.
+ Instruction* MaybeAddEscapeHatch(CodeGen* gen,
+ bool* has_unsafe_traps,
+ Instruction* rest);
+
+ // Return an instruction sequence that loads and checks the system
+ // call number, performs a binary search, and then dispatches to an
+ // appropriate instruction sequence compiled from the current
+ // policy.
+ Instruction* DispatchSyscall(CodeGen* gen);
+
+ // Return an instruction sequence that checks the system call number
+ // (expected to be loaded in register A) and if valid, passes
+ // control to |passed| (with register A still valid).
+ Instruction* CheckSyscallNumber(CodeGen* gen, Instruction* passed);
// Verify the correctness of a compiled program by comparing it against the
// current policy. This function should only ever be called by unit tests and
// sorted in ascending order of system call numbers. There are no gaps in the
// ranges. System calls with identical ErrorCodes are coalesced into a single
// range.
- void FindRanges(Ranges *ranges);
+ void FindRanges(Ranges* ranges);
// Returns a BPF program snippet that implements a jump table for the
// given range of system call numbers. This function runs recursively.
- Instruction *AssembleJumpTable(CodeGen *gen,
+ Instruction* AssembleJumpTable(CodeGen* gen,
Ranges::const_iterator start,
Ranges::const_iterator stop);
// conditional expression; if so, this function will recursively call
// CondExpression() and possibly RetExpression() to build a complex set of
// instructions.
- Instruction *RetExpression(CodeGen *gen, const ErrorCode& err);
+ Instruction* RetExpression(CodeGen* gen, const ErrorCode& err);
// Returns a BPF program that evaluates the conditional expression in
// "cond" and returns the appropriate value from the BPF filter program.
// This function recursively calls RetExpression(); it should only ever be
// called from RetExpression().
- Instruction *CondExpression(CodeGen *gen, const ErrorCode& cond);
+ Instruction* CondExpression(CodeGen* gen, const ErrorCode& cond);
+
+ // Returns a BPF program that evaluates half of a conditional expression;
+ // it should only ever be called from CondExpression().
+ Instruction* CondExpressionHalf(CodeGen* gen,
+ const ErrorCode& cond,
+ ArgHalf half,
+ Instruction* passed,
+ Instruction* failed);
static SandboxStatus status_;
- bool quiet_;
- int proc_fd_;
- Evaluators *evaluators_;
- Conds *conds_;
+ bool quiet_;
+ int proc_fd_;
+ scoped_ptr<const SandboxBPFPolicy> policy_;
+ Conds* conds_;
+ bool sandbox_has_started_;
- DISALLOW_COPY_AND_ASSIGN(Sandbox);
+ DISALLOW_COPY_AND_ASSIGN(SandboxBPF);
};
-} // namespace
+} // namespace sandbox
#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__