Home | History | Annotate | Download | only in seccomp-bpf
      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 <stdint.h>
      9 
     10 #include <map>
     11 #include <set>
     12 #include <vector>
     13 
     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"
     19 
     20 struct sock_filter;
     21 
     22 namespace sandbox {
     23 class CodeGen;
     24 class SandboxBPFPolicy;
     25 class SandboxUnittestHelper;
     26 struct Instruction;
     27 
     28 class SANDBOX_EXPORT SandboxBPF {
     29  public:
     30   enum SandboxStatus {
     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
     36   };
     37 
     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 {
     43     PROCESS_INVALID,
     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.
     48   };
     49 
     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;
     53 
     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.
     64   SandboxBPF();
     65   ~SandboxBPF();
     66 
     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
     69   // system calls.
     70   static bool IsValidSyscallNumber(int sysnum);
     71 
     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);
     78 
     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();
     82 
     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);
     89 
     90   // Set the BPF policy as |policy|. Ownership of |policy| is transfered here
     91   // to the sandbox object.
     92   void SetSandboxPolicy(SandboxBPFPolicy* policy);
     93 
     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
     96   // ErrorCode object.
     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()
     99   // handler.
    100   static ErrorCode Trap(Trap::TrapFnc fnc, const void* aux);
    101 
    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);
    113 
    114   // UnsafeTraps require some syscalls to always be allowed.
    115   // This helper function returns true for these calls.
    116   static bool IsRequiredForUnsafeTrap(int sysno);
    117 
    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);
    127 
    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
    136   // message).
    137   ErrorCode CondMaskedEqual(int argno,
    138                             ErrorCode::ArgType is_32bit,
    139                             uint64_t mask,
    140                             uint64_t value,
    141                             const ErrorCode& passed,
    142                             const ErrorCode& failed);
    143 
    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,
    149                  uint64_t value,
    150                  const ErrorCode& passed,
    151                  const ErrorCode& failed);
    152 
    153   // Kill the program and print an error message.
    154   static ErrorCode Kill(const char* msg);
    155 
    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
    166   // disallowed.
    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;
    170 
    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);
    180 
    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();
    185 
    186  private:
    187   friend class CodeGen;
    188   friend class SandboxUnittestHelper;
    189   friend class ErrorCode;
    190 
    191   struct Range {
    192     Range(uint32_t f, uint32_t t, const ErrorCode& e)
    193         : from(f), to(t), err(e) {}
    194     uint32_t from, to;
    195     ErrorCode err;
    196   };
    197   typedef std::vector<Range> Ranges;
    198   typedef std::map<uint32_t, ErrorCode> ErrMap;
    199   typedef std::set<ErrorCode, struct ErrorCode::LessThan> Conds;
    200 
    201   // Used by CondExpressionHalf to track which half of the argument it's
    202   // emitting instructions for.
    203   enum ArgHalf {
    204     LowerHalf,
    205     UpperHalf,
    206   };
    207 
    208   // Get a file descriptor pointing to "/proc", if currently available.
    209   int proc_fd() { return proc_fd_; }
    210 
    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);
    216 
    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();
    222 
    223   // Verify that the current policy passes some basic sanity checks.
    224   void PolicySanityChecks(SandboxBPFPolicy* policy);
    225 
    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);
    229 
    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);
    233 
    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);
    238 
    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|.
    243   //
    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,
    248                                    Instruction* rest);
    249 
    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
    253   // policy.
    254   Instruction* DispatchSyscall(CodeGen* gen);
    255 
    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);
    260 
    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);
    265 
    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
    269   // range.
    270   void FindRanges(Ranges* ranges);
    271 
    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);
    277 
    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
    282   // instructions.
    283   Instruction* RetExpression(CodeGen* gen, const ErrorCode& err);
    284 
    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);
    290 
    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,
    295                                   ArgHalf half,
    296                                   Instruction* passed,
    297                                   Instruction* failed);
    298 
    299   static SandboxStatus status_;
    300 
    301   bool quiet_;
    302   int proc_fd_;
    303   scoped_ptr<const SandboxBPFPolicy> policy_;
    304   Conds* conds_;
    305   bool sandbox_has_started_;
    306 
    307   DISALLOW_COPY_AND_ASSIGN(SandboxBPF);
    308 };
    309 
    310 }  // namespace sandbox
    311 
    312 #endif  // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
    313