OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__ | 5 #ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_ |
6 #define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__ | 6 #define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_ |
7 | 7 |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 | 9 |
10 #include "base/compiler_specific.h" | 10 #include "base/files/scoped_file.h" |
11 #include "base/macros.h" | 11 #include "base/macros.h" |
12 #include "base/memory/scoped_ptr.h" | 12 #include "base/memory/scoped_ptr.h" |
13 #include "sandbox/linux/seccomp-bpf/codegen.h" | 13 #include "sandbox/linux/seccomp-bpf/codegen.h" |
14 #include "sandbox/sandbox_export.h" | 14 #include "sandbox/sandbox_export.h" |
15 | 15 |
16 namespace sandbox { | 16 namespace sandbox { |
17 struct arch_seccomp_data; | 17 struct arch_seccomp_data; |
18 namespace bpf_dsl { | 18 namespace bpf_dsl { |
19 class Policy; | 19 class Policy; |
20 } | 20 } |
21 | 21 |
| 22 // This class can be used to apply a syscall sandboxing policy expressed in a |
| 23 // bpf_dsl::Policy object to the current process. |
| 24 // Syscall sandboxing policies get inherited by subprocesses and, once applied, |
| 25 // can never be removed for the lifetime of the process. |
22 class SANDBOX_EXPORT SandboxBPF { | 26 class SANDBOX_EXPORT SandboxBPF { |
23 public: | 27 public: |
24 enum SandboxStatus { | 28 enum class SeccompLevel { |
25 STATUS_UNKNOWN, // Status prior to calling supportsSeccompSandbox() | 29 SINGLE_THREADED, |
26 STATUS_UNSUPPORTED, // The kernel does not appear to support sandboxing | 30 MULTI_THREADED, |
27 STATUS_UNAVAILABLE, // Currently unavailable but might work again later | |
28 STATUS_AVAILABLE, // Sandboxing is available but not currently active | |
29 STATUS_ENABLED // The sandbox is now active | |
30 }; | 31 }; |
31 | 32 |
32 // Depending on the level of kernel support, seccomp-bpf may require the | 33 // Ownership of |policy| is transfered here to the sandbox object. |
33 // process to be single-threaded in order to enable it. When calling | 34 // nullptr is allowed for unit tests. |
34 // StartSandbox(), the program should indicate whether or not the sandbox | 35 explicit SandboxBPF(bpf_dsl::Policy* policy); |
35 // should try and engage with multi-thread support. | |
36 enum SandboxThreadState { | |
37 PROCESS_INVALID, | |
38 PROCESS_SINGLE_THREADED, // The program is currently single-threaded. | |
39 // Note: PROCESS_MULTI_THREADED requires experimental kernel support that | |
40 // has not been contributed to upstream Linux. | |
41 PROCESS_MULTI_THREADED, // The program may be multi-threaded. | |
42 }; | |
43 | |
44 // Constructors and destructors. | |
45 // NOTE: Setting a policy and starting the sandbox is a one-way operation. | 36 // NOTE: Setting a policy and starting the sandbox is a one-way operation. |
46 // The kernel does not provide any option for unloading a loaded | 37 // The kernel does not provide any option for unloading a loaded sandbox. The |
47 // sandbox. Strictly speaking, that means we should disallow calling | 38 // sandbox remains engaged even when the object is destructed. |
48 // the destructor, if StartSandbox() has ever been called. In practice, | |
49 // this makes it needlessly complicated to operate on "Sandbox" | |
50 // objects. So, we instead opted to allow object destruction. But it | |
51 // should be noted that during its lifetime, the object probably made | |
52 // irreversible state changes to the runtime environment. These changes | |
53 // stay in effect even after the destructor has been run. | |
54 SandboxBPF(); | |
55 ~SandboxBPF(); | 39 ~SandboxBPF(); |
56 | 40 |
| 41 // Detect if the kernel supports the specified seccomp level. |
| 42 // See StartSandbox() for a description of these. |
| 43 static bool SupportsSeccompSandbox(SeccompLevel level); |
| 44 |
| 45 // This is the main public entry point. It sets up the resources needed by |
| 46 // the sandbox, and enters Seccomp mode. |
| 47 // The calling process must provide a |level| to tell the sandbox which type |
| 48 // of kernel support it should engage. |
| 49 // SINGLE_THREADED will only sandbox the calling thread. Since it would be a |
| 50 // security risk, the sandbox will also check that the current process is |
| 51 // single threaded and crash if it isn't the case. |
| 52 // MULTI_THREADED requires more recent kernel support and allows to sandbox |
| 53 // all the threads of the current process. Be mindful of potential races, |
| 54 // with other threads using disallowed system calls either before or after |
| 55 // the sandbox is engaged. |
| 56 // |
| 57 // It is possible to stack multiple sandboxes by creating separate "Sandbox" |
| 58 // objects and calling "StartSandbox()" on each of them. Please note, that |
| 59 // this requires special care, though, as newly stacked sandboxes can never |
| 60 // relax restrictions imposed by earlier sandboxes. Furthermore, installing |
| 61 // a new policy requires making system calls, that might already be |
| 62 // disallowed. |
| 63 // Finally, stacking does add more kernel overhead than having a single |
| 64 // combined policy. So, it should only be used if there are no alternatives. |
| 65 bool StartSandbox(SeccompLevel level) WARN_UNUSED_RESULT; |
| 66 |
| 67 // The sandbox needs to be able to access files in "/proc/self/task/". If |
| 68 // this directory is not accessible when "StartSandbox()" gets called, the |
| 69 // caller must provide an already opened file descriptor by calling |
| 70 // "SetProcTaskFd()". |
| 71 // The sandbox becomes the new owner of this file descriptor and will |
| 72 // close it when "StartSandbox()" executes or when the sandbox object |
| 73 // disappears. |
| 74 void SetProcTaskFd(base::ScopedFD proc_task_fd); |
| 75 |
57 // Checks whether a particular system call number is valid on the current | 76 // Checks whether a particular system call number is valid on the current |
58 // architecture. E.g. on ARM there's a non-contiguous range of private | 77 // architecture. |
59 // system calls. | |
60 static bool IsValidSyscallNumber(int sysnum); | 78 static bool IsValidSyscallNumber(int sysnum); |
61 | 79 |
62 // There are a lot of reasons why the Seccomp sandbox might not be available. | |
63 // This could be because the kernel does not support Seccomp mode, or it | |
64 // could be because another sandbox is already active. | |
65 // "proc_fd" should be a file descriptor for "/proc", or -1 if not | |
66 // provided by the caller. | |
67 static SandboxStatus SupportsSeccompSandbox(int proc_fd); | |
68 | |
69 // Determines if the kernel has support for the seccomp() system call to | |
70 // synchronize BPF filters across a thread group. | |
71 static SandboxStatus SupportsSeccompThreadFilterSynchronization(); | |
72 | |
73 // The sandbox needs to be able to access files in "/proc/self". If this | |
74 // directory is not accessible when "startSandbox()" gets called, the caller | |
75 // can provide an already opened file descriptor by calling "set_proc_fd()". | |
76 // The sandbox becomes the new owner of this file descriptor and will | |
77 // eventually close it when "StartSandbox()" executes. | |
78 void set_proc_fd(int proc_fd); | |
79 | |
80 // Set the BPF policy as |policy|. Ownership of |policy| is transfered here | |
81 // to the sandbox object. | |
82 void SetSandboxPolicy(bpf_dsl::Policy* policy); | |
83 | |
84 // UnsafeTraps require some syscalls to always be allowed. | 80 // UnsafeTraps require some syscalls to always be allowed. |
85 // This helper function returns true for these calls. | 81 // This helper function returns true for these calls. |
86 static bool IsRequiredForUnsafeTrap(int sysno); | 82 static bool IsRequiredForUnsafeTrap(int sysno); |
87 | 83 |
88 // From within an UnsafeTrap() it is often useful to be able to execute | 84 // From within an UnsafeTrap() it is often useful to be able to execute |
89 // the system call that triggered the trap. The ForwardSyscall() method | 85 // the system call that triggered the trap. The ForwardSyscall() method |
90 // makes this easy. It is more efficient than calling glibc's syscall() | 86 // makes this easy. It is more efficient than calling glibc's syscall() |
91 // function, as it avoid the extra round-trip to the signal handler. And | 87 // function, as it avoid the extra round-trip to the signal handler. And |
92 // it automatically does the correct thing to report kernel-style error | 88 // it automatically does the correct thing to report kernel-style error |
93 // conditions, rather than setting errno. See the comments for TrapFnc for | 89 // conditions, rather than setting errno. See the comments for TrapFnc for |
94 // details. In other words, the return value from ForwardSyscall() is | 90 // details. In other words, the return value from ForwardSyscall() is |
95 // directly suitable as a return value for a trap handler. | 91 // directly suitable as a return value for a trap handler. |
96 static intptr_t ForwardSyscall(const struct arch_seccomp_data& args); | 92 static intptr_t ForwardSyscall(const struct arch_seccomp_data& args); |
97 | 93 |
98 // This is the main public entry point. It finds all system calls that | |
99 // need rewriting, sets up the resources needed by the sandbox, and | |
100 // enters Seccomp mode. | |
101 // The calling process must specify its current SandboxThreadState, as a way | |
102 // to tell the sandbox which type of kernel support it should engage. | |
103 // It is possible to stack multiple sandboxes by creating separate "Sandbox" | |
104 // objects and calling "StartSandbox()" on each of them. Please note, that | |
105 // this requires special care, though, as newly stacked sandboxes can never | |
106 // relax restrictions imposed by earlier sandboxes. Furthermore, installing | |
107 // a new policy requires making system calls, that might already be | |
108 // disallowed. | |
109 // Finally, stacking does add more kernel overhead than having a single | |
110 // combined policy. So, it should only be used if there are no alternatives. | |
111 bool StartSandbox(SandboxThreadState thread_state) WARN_UNUSED_RESULT; | |
112 | |
113 // Assembles a BPF filter program from the current policy. After calling this | 94 // Assembles a BPF filter program from the current policy. After calling this |
114 // function, you must not call any other sandboxing function. | 95 // function, you must not call any other sandboxing function. |
115 // Typically, AssembleFilter() is only used by unit tests and by sandbox | 96 // Typically, AssembleFilter() is only used by unit tests and by sandbox |
116 // internals. It should not be used by production code. | 97 // internals. It should not be used by production code. |
117 // For performance reasons, we normally only run the assembled BPF program | 98 // For performance reasons, we normally only run the assembled BPF program |
118 // through the verifier, iff the program was built in debug mode. | 99 // through the verifier, iff the program was built in debug mode. |
119 // But by setting "force_verification", the caller can request that the | 100 // But by setting "force_verification", the caller can request that the |
120 // verifier is run unconditionally. This is useful for unittests. | 101 // verifier is run unconditionally. This is useful for unittests. |
121 scoped_ptr<CodeGen::Program> AssembleFilter(bool force_verification); | 102 scoped_ptr<CodeGen::Program> AssembleFilter(bool force_verification); |
122 | 103 |
123 private: | 104 private: |
124 // Get a file descriptor pointing to "/proc", if currently available. | |
125 int proc_fd() { return proc_fd_; } | |
126 | |
127 // Creates a subprocess and runs "code_in_sandbox" inside of the specified | |
128 // policy. The caller has to make sure that "this" has not yet been | |
129 // initialized with any other policies. | |
130 bool RunFunctionInPolicy(void (*code_in_sandbox)(), | |
131 scoped_ptr<bpf_dsl::Policy> policy); | |
132 | |
133 // Performs a couple of sanity checks to verify that the kernel supports the | |
134 // features that we need for successful sandboxing. | |
135 // The caller has to make sure that "this" has not yet been initialized with | |
136 // any other policies. | |
137 bool KernelSupportSeccompBPF(); | |
138 | |
139 // Assembles and installs a filter based on the policy that has previously | 105 // Assembles and installs a filter based on the policy that has previously |
140 // been configured with SetSandboxPolicy(). | 106 // been configured with SetSandboxPolicy(). |
141 void InstallFilter(bool must_sync_threads); | 107 void InstallFilter(bool must_sync_threads); |
142 | 108 |
143 // Verify the correctness of a compiled program by comparing it against the | 109 base::ScopedFD proc_task_fd_; |
144 // current policy. This function should only ever be called by unit tests and | |
145 // by the sandbox internals. It should not be used by production code. | |
146 void VerifyProgram(const CodeGen::Program& program); | |
147 | |
148 static SandboxStatus status_; | |
149 | |
150 bool quiet_; | |
151 int proc_fd_; | |
152 bool sandbox_has_started_; | 110 bool sandbox_has_started_; |
153 scoped_ptr<bpf_dsl::Policy> policy_; | 111 scoped_ptr<bpf_dsl::Policy> policy_; |
154 | 112 |
155 DISALLOW_COPY_AND_ASSIGN(SandboxBPF); | 113 DISALLOW_COPY_AND_ASSIGN(SandboxBPF); |
156 }; | 114 }; |
157 | 115 |
158 } // namespace sandbox | 116 } // namespace sandbox |
159 | 117 |
160 #endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__ | 118 #endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_ |
OLD | NEW |