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 <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