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 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" | 5 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" |
6 | 6 |
7 // Some headers on Android are missing cdefs: crbug.com/172337. | 7 // Some headers on Android are missing cdefs: crbug.com/172337. |
8 // (We can't use OS_ANDROID here since build_config.h is not included). | 8 // (We can't use OS_ANDROID here since build_config.h is not included). |
9 #if defined(ANDROID) | 9 #if defined(ANDROID) |
10 #include <sys/cdefs.h> | 10 #include <sys/cdefs.h> |
11 #endif | 11 #endif |
12 | 12 |
13 #include <errno.h> | 13 #include <errno.h> |
14 #include <fcntl.h> | |
15 #include <linux/filter.h> | 14 #include <linux/filter.h> |
16 #include <signal.h> | |
17 #include <string.h> | |
18 #include <sys/prctl.h> | 15 #include <sys/prctl.h> |
19 #include <sys/stat.h> | |
20 #include <sys/syscall.h> | |
21 #include <sys/types.h> | 16 #include <sys/types.h> |
22 #include <sys/wait.h> | |
23 #include <time.h> | |
24 #include <unistd.h> | 17 #include <unistd.h> |
25 | 18 |
26 #include "base/compiler_specific.h" | 19 #include "base/compiler_specific.h" |
| 20 #include "base/files/scoped_file.h" |
27 #include "base/logging.h" | 21 #include "base/logging.h" |
28 #include "base/macros.h" | 22 #include "base/macros.h" |
29 #include "base/memory/scoped_ptr.h" | 23 #include "base/memory/scoped_ptr.h" |
30 #include "base/posix/eintr_wrapper.h" | 24 #include "base/posix/eintr_wrapper.h" |
31 #include "sandbox/linux/bpf_dsl/bpf_dsl.h" | 25 #include "base/third_party/valgrind/valgrind.h" |
32 #include "sandbox/linux/bpf_dsl/dump_bpf.h" | 26 #include "sandbox/linux/bpf_dsl/dump_bpf.h" |
33 #include "sandbox/linux/bpf_dsl/policy.h" | 27 #include "sandbox/linux/bpf_dsl/policy.h" |
34 #include "sandbox/linux/bpf_dsl/policy_compiler.h" | 28 #include "sandbox/linux/bpf_dsl/policy_compiler.h" |
35 #include "sandbox/linux/seccomp-bpf/codegen.h" | 29 #include "sandbox/linux/seccomp-bpf/codegen.h" |
36 #include "sandbox/linux/seccomp-bpf/die.h" | 30 #include "sandbox/linux/seccomp-bpf/die.h" |
37 #include "sandbox/linux/seccomp-bpf/errorcode.h" | 31 #include "sandbox/linux/seccomp-bpf/errorcode.h" |
38 #include "sandbox/linux/seccomp-bpf/linux_seccomp.h" | 32 #include "sandbox/linux/seccomp-bpf/linux_seccomp.h" |
39 #include "sandbox/linux/seccomp-bpf/syscall.h" | 33 #include "sandbox/linux/seccomp-bpf/syscall.h" |
40 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" | 34 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" |
41 #include "sandbox/linux/seccomp-bpf/trap.h" | 35 #include "sandbox/linux/seccomp-bpf/trap.h" |
42 #include "sandbox/linux/seccomp-bpf/verifier.h" | 36 #include "sandbox/linux/seccomp-bpf/verifier.h" |
43 #include "sandbox/linux/services/linux_syscalls.h" | 37 #include "sandbox/linux/services/linux_syscalls.h" |
44 #include "sandbox/linux/services/syscall_wrappers.h" | 38 #include "sandbox/linux/services/syscall_wrappers.h" |
45 | 39 #include "sandbox/linux/services/thread_helpers.h" |
46 using sandbox::bpf_dsl::Allow; | |
47 using sandbox::bpf_dsl::Error; | |
48 using sandbox::bpf_dsl::ResultExpr; | |
49 | 40 |
50 namespace sandbox { | 41 namespace sandbox { |
51 | 42 |
52 namespace { | 43 namespace { |
53 | 44 |
54 const int kExpectedExitCode = 100; | 45 bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; } |
55 | 46 |
56 #if !defined(NDEBUG) | 47 bool IsSingleThreaded(int proc_task_fd) { |
57 void WriteFailedStderrSetupMessage(int out_fd) { | 48 return ThreadHelpers::IsSingleThreaded(proc_task_fd); |
58 const char* error_string = strerror(errno); | |
59 static const char msg[] = | |
60 "You have reproduced a puzzling issue.\n" | |
61 "Please, report to crbug.com/152530!\n" | |
62 "Failed to set up stderr: "; | |
63 if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg) - 1)) > 0 && error_string && | |
64 HANDLE_EINTR(write(out_fd, error_string, strlen(error_string))) > 0 && | |
65 HANDLE_EINTR(write(out_fd, "\n", 1))) { | |
66 } | |
67 } | |
68 #endif // !defined(NDEBUG) | |
69 | |
70 // We define a really simple sandbox policy. It is just good enough for us | |
71 // to tell that the sandbox has actually been activated. | |
72 class ProbePolicy : public bpf_dsl::Policy { | |
73 public: | |
74 ProbePolicy() {} | |
75 virtual ~ProbePolicy() {} | |
76 | |
77 virtual ResultExpr EvaluateSyscall(int sysnum) const override { | |
78 switch (sysnum) { | |
79 case __NR_getpid: | |
80 // Return EPERM so that we can check that the filter actually ran. | |
81 return Error(EPERM); | |
82 case __NR_exit_group: | |
83 // Allow exit() with a non-default return code. | |
84 return Allow(); | |
85 default: | |
86 // Make everything else fail in an easily recognizable way. | |
87 return Error(EINVAL); | |
88 } | |
89 } | |
90 | |
91 private: | |
92 DISALLOW_COPY_AND_ASSIGN(ProbePolicy); | |
93 }; | |
94 | |
95 void ProbeProcess(void) { | |
96 if (sys_getpid() < 0 && errno == EPERM) { | |
97 sys_exit_group(kExpectedExitCode); | |
98 } | |
99 } | 49 } |
100 | 50 |
101 class AllowAllPolicy : public bpf_dsl::Policy { | 51 // Check if the kernel supports seccomp-filter (a.k.a. seccomp mode 2) via |
102 public: | 52 // prctl(). |
103 AllowAllPolicy() {} | 53 bool KernelSupportsSeccompBPF() { |
104 virtual ~AllowAllPolicy() {} | 54 errno = 0; |
| 55 const int rv = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr); |
105 | 56 |
106 virtual ResultExpr EvaluateSyscall(int sysnum) const override { | 57 if (rv == -1 && EFAULT == errno) { |
107 DCHECK(SandboxBPF::IsValidSyscallNumber(sysnum)); | 58 return true; |
108 return Allow(); | |
109 } | 59 } |
110 | 60 return false; |
111 private: | |
112 DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy); | |
113 }; | |
114 | |
115 void TryVsyscallProcess(void) { | |
116 time_t current_time; | |
117 // time() is implemented as a vsyscall. With an older glibc, with | |
118 // vsyscall=emulate and some versions of the seccomp BPF patch | |
119 // we may get SIGKILL-ed. Detect this! | |
120 if (time(¤t_time) != static_cast<time_t>(-1)) { | |
121 sys_exit_group(kExpectedExitCode); | |
122 } | |
123 } | 61 } |
124 | 62 |
125 bool IsSingleThreaded(int proc_fd) { | 63 // Check if the kernel supports seccomp-filter via the seccomp system call |
126 if (proc_fd < 0) { | 64 // and the TSYNC feature to enable seccomp on all threads. |
127 // Cannot determine whether program is single-threaded. Hope for | 65 bool KernelSupportsSeccompTsync() { |
128 // the best... | 66 errno = 0; |
| 67 const int rv = |
| 68 sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, nullptr); |
| 69 |
| 70 if (rv == -1 && errno == EFAULT) { |
129 return true; | 71 return true; |
130 } | 72 } else { |
131 | 73 // TODO(jln): turn these into DCHECK after 417888 is considered fixed. |
132 struct stat sb; | 74 CHECK_EQ(-1, rv); |
133 int task = -1; | 75 CHECK(ENOSYS == errno || EINVAL == errno); |
134 if ((task = openat(proc_fd, "self/task", O_RDONLY | O_DIRECTORY)) < 0 || | |
135 fstat(task, &sb) != 0 || sb.st_nlink != 3 || IGNORE_EINTR(close(task))) { | |
136 if (task >= 0) { | |
137 if (IGNORE_EINTR(close(task))) { | |
138 } | |
139 } | |
140 return false; | 76 return false; |
141 } | 77 } |
142 return true; | |
143 } | 78 } |
144 | 79 |
145 } // namespace | 80 } // namespace |
146 | 81 |
147 SandboxBPF::SandboxBPF() | 82 SandboxBPF::SandboxBPF(bpf_dsl::Policy* policy) |
148 : quiet_(false), proc_fd_(-1), sandbox_has_started_(false), policy_() { | 83 : proc_task_fd_(), sandbox_has_started_(false), policy_(policy) { |
149 } | 84 } |
150 | 85 |
151 SandboxBPF::~SandboxBPF() { | 86 SandboxBPF::~SandboxBPF() { |
152 } | 87 } |
153 | 88 |
154 bool SandboxBPF::IsValidSyscallNumber(int sysnum) { | 89 // static |
155 return SyscallSet::IsValid(sysnum); | 90 bool SandboxBPF::SupportsSeccompSandbox(SeccompLevel level) { |
| 91 // Never pretend to support seccomp with Valgrind, as it |
| 92 // throws the tool off. |
| 93 if (IsRunningOnValgrind()) { |
| 94 return false; |
| 95 } |
| 96 |
| 97 switch (level) { |
| 98 case SeccompLevel::SINGLE_THREADED: |
| 99 return KernelSupportsSeccompBPF(); |
| 100 case SeccompLevel::MULTI_THREADED: |
| 101 return KernelSupportsSeccompTsync(); |
| 102 } |
| 103 NOTREACHED(); |
| 104 return false; |
156 } | 105 } |
157 | 106 |
158 bool SandboxBPF::RunFunctionInPolicy(void (*code_in_sandbox)(), | 107 bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level) { |
159 scoped_ptr<bpf_dsl::Policy> policy) { | 108 DCHECK(policy_); |
160 // Block all signals before forking a child process. This prevents an | 109 CHECK(seccomp_level == SeccompLevel::SINGLE_THREADED || |
161 // attacker from manipulating our test by sending us an unexpected signal. | 110 seccomp_level == SeccompLevel::MULTI_THREADED); |
162 sigset_t old_mask, new_mask; | |
163 if (sigfillset(&new_mask) || sigprocmask(SIG_BLOCK, &new_mask, &old_mask)) { | |
164 SANDBOX_DIE("sigprocmask() failed"); | |
165 } | |
166 int fds[2]; | |
167 if (pipe2(fds, O_NONBLOCK | O_CLOEXEC)) { | |
168 SANDBOX_DIE("pipe() failed"); | |
169 } | |
170 | 111 |
171 if (fds[0] <= 2 || fds[1] <= 2) { | 112 if (sandbox_has_started_) { |
172 SANDBOX_DIE("Process started without standard file descriptors"); | |
173 } | |
174 | |
175 // This code is using fork() and should only ever run single-threaded. | |
176 // Most of the code below is "async-signal-safe" and only minor changes | |
177 // would be needed to support threads. | |
178 DCHECK(IsSingleThreaded(proc_fd_)); | |
179 pid_t pid = fork(); | |
180 if (pid < 0) { | |
181 // Die if we cannot fork(). We would probably fail a little later | |
182 // anyway, as the machine is likely very close to running out of | |
183 // memory. | |
184 // But what we don't want to do is return "false", as a crafty | |
185 // attacker might cause fork() to fail at will and could trick us | |
186 // into running without a sandbox. | |
187 sigprocmask(SIG_SETMASK, &old_mask, NULL); // OK, if it fails | |
188 SANDBOX_DIE("fork() failed unexpectedly"); | |
189 } | |
190 | |
191 // In the child process | |
192 if (!pid) { | |
193 // Test a very simple sandbox policy to verify that we can | |
194 // successfully turn on sandboxing. | |
195 Die::EnableSimpleExit(); | |
196 | |
197 errno = 0; | |
198 if (IGNORE_EINTR(close(fds[0]))) { | |
199 // This call to close() has been failing in strange ways. See | |
200 // crbug.com/152530. So we only fail in debug mode now. | |
201 #if !defined(NDEBUG) | |
202 WriteFailedStderrSetupMessage(fds[1]); | |
203 SANDBOX_DIE(NULL); | |
204 #endif | |
205 } | |
206 if (HANDLE_EINTR(dup2(fds[1], 2)) != 2) { | |
207 // Stderr could very well be a file descriptor to .xsession-errors, or | |
208 // another file, which could be backed by a file system that could cause | |
209 // dup2 to fail while trying to close stderr. It's important that we do | |
210 // not fail on trying to close stderr. | |
211 // If dup2 fails here, we will continue normally, this means that our | |
212 // parent won't cause a fatal failure if something writes to stderr in | |
213 // this child. | |
214 #if !defined(NDEBUG) | |
215 // In DEBUG builds, we still want to get a report. | |
216 WriteFailedStderrSetupMessage(fds[1]); | |
217 SANDBOX_DIE(NULL); | |
218 #endif | |
219 } | |
220 if (IGNORE_EINTR(close(fds[1]))) { | |
221 // This call to close() has been failing in strange ways. See | |
222 // crbug.com/152530. So we only fail in debug mode now. | |
223 #if !defined(NDEBUG) | |
224 WriteFailedStderrSetupMessage(fds[1]); | |
225 SANDBOX_DIE(NULL); | |
226 #endif | |
227 } | |
228 | |
229 SetSandboxPolicy(policy.release()); | |
230 if (!StartSandbox(PROCESS_SINGLE_THREADED)) { | |
231 SANDBOX_DIE(NULL); | |
232 } | |
233 | |
234 // Run our code in the sandbox. | |
235 code_in_sandbox(); | |
236 | |
237 // code_in_sandbox() is not supposed to return here. | |
238 SANDBOX_DIE(NULL); | |
239 } | |
240 | |
241 // In the parent process. | |
242 if (IGNORE_EINTR(close(fds[1]))) { | |
243 SANDBOX_DIE("close() failed"); | |
244 } | |
245 if (sigprocmask(SIG_SETMASK, &old_mask, NULL)) { | |
246 SANDBOX_DIE("sigprocmask() failed"); | |
247 } | |
248 int status; | |
249 if (HANDLE_EINTR(waitpid(pid, &status, 0)) != pid) { | |
250 SANDBOX_DIE("waitpid() failed unexpectedly"); | |
251 } | |
252 bool rc = WIFEXITED(status) && WEXITSTATUS(status) == kExpectedExitCode; | |
253 | |
254 // If we fail to support sandboxing, there might be an additional | |
255 // error message. If so, this was an entirely unexpected and fatal | |
256 // failure. We should report the failure and somebody must fix | |
257 // things. This is probably a security-critical bug in the sandboxing | |
258 // code. | |
259 if (!rc) { | |
260 char buf[4096]; | |
261 ssize_t len = HANDLE_EINTR(read(fds[0], buf, sizeof(buf) - 1)); | |
262 if (len > 0) { | |
263 while (len > 1 && buf[len - 1] == '\n') { | |
264 --len; | |
265 } | |
266 buf[len] = '\000'; | |
267 SANDBOX_DIE(buf); | |
268 } | |
269 } | |
270 if (IGNORE_EINTR(close(fds[0]))) { | |
271 SANDBOX_DIE("close() failed"); | |
272 } | |
273 | |
274 return rc; | |
275 } | |
276 | |
277 bool SandboxBPF::KernelSupportSeccompBPF() { | |
278 return RunFunctionInPolicy(ProbeProcess, | |
279 scoped_ptr<bpf_dsl::Policy>(new ProbePolicy())) && | |
280 RunFunctionInPolicy(TryVsyscallProcess, | |
281 scoped_ptr<bpf_dsl::Policy>(new AllowAllPolicy())); | |
282 } | |
283 | |
284 // static | |
285 SandboxBPF::SandboxStatus SandboxBPF::SupportsSeccompSandbox(int proc_fd) { | |
286 // It the sandbox is currently active, we clearly must have support for | |
287 // sandboxing. | |
288 if (status_ == STATUS_ENABLED) { | |
289 return status_; | |
290 } | |
291 | |
292 // Even if the sandbox was previously available, something might have | |
293 // changed in our run-time environment. Check one more time. | |
294 if (status_ == STATUS_AVAILABLE) { | |
295 if (!IsSingleThreaded(proc_fd)) { | |
296 status_ = STATUS_UNAVAILABLE; | |
297 } | |
298 return status_; | |
299 } | |
300 | |
301 if (status_ == STATUS_UNAVAILABLE && IsSingleThreaded(proc_fd)) { | |
302 // All state transitions resulting in STATUS_UNAVAILABLE are immediately | |
303 // preceded by STATUS_AVAILABLE. Furthermore, these transitions all | |
304 // happen, if and only if they are triggered by the process being multi- | |
305 // threaded. | |
306 // In other words, if a single-threaded process is currently in the | |
307 // STATUS_UNAVAILABLE state, it is safe to assume that sandboxing is | |
308 // actually available. | |
309 status_ = STATUS_AVAILABLE; | |
310 return status_; | |
311 } | |
312 | |
313 // If we have not previously checked for availability of the sandbox or if | |
314 // we otherwise don't believe to have a good cached value, we have to | |
315 // perform a thorough check now. | |
316 if (status_ == STATUS_UNKNOWN) { | |
317 // We create our own private copy of a "Sandbox" object. This ensures that | |
318 // the object does not have any policies configured, that might interfere | |
319 // with the tests done by "KernelSupportSeccompBPF()". | |
320 SandboxBPF sandbox; | |
321 | |
322 // By setting "quiet_ = true" we suppress messages for expected and benign | |
323 // failures (e.g. if the current kernel lacks support for BPF filters). | |
324 sandbox.quiet_ = true; | |
325 sandbox.set_proc_fd(proc_fd); | |
326 status_ = sandbox.KernelSupportSeccompBPF() ? STATUS_AVAILABLE | |
327 : STATUS_UNSUPPORTED; | |
328 | |
329 // As we are performing our tests from a child process, the run-time | |
330 // environment that is visible to the sandbox is always guaranteed to be | |
331 // single-threaded. Let's check here whether the caller is single- | |
332 // threaded. Otherwise, we mark the sandbox as temporarily unavailable. | |
333 if (status_ == STATUS_AVAILABLE && !IsSingleThreaded(proc_fd)) { | |
334 status_ = STATUS_UNAVAILABLE; | |
335 } | |
336 } | |
337 return status_; | |
338 } | |
339 | |
340 // static | |
341 SandboxBPF::SandboxStatus | |
342 SandboxBPF::SupportsSeccompThreadFilterSynchronization() { | |
343 // Applying NO_NEW_PRIVS, a BPF filter, and synchronizing the filter across | |
344 // the thread group are all handled atomically by this syscall. | |
345 const int rv = syscall( | |
346 __NR_seccomp, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, NULL); | |
347 | |
348 if (rv == -1 && errno == EFAULT) { | |
349 return STATUS_AVAILABLE; | |
350 } else { | |
351 // TODO(jln): turn these into DCHECK after 417888 is considered fixed. | |
352 CHECK_EQ(-1, rv); | |
353 CHECK(ENOSYS == errno || EINVAL == errno); | |
354 return STATUS_UNSUPPORTED; | |
355 } | |
356 } | |
357 | |
358 void SandboxBPF::set_proc_fd(int proc_fd) { proc_fd_ = proc_fd; } | |
359 | |
360 bool SandboxBPF::StartSandbox(SandboxThreadState thread_state) { | |
361 CHECK(thread_state == PROCESS_SINGLE_THREADED || | |
362 thread_state == PROCESS_MULTI_THREADED); | |
363 | |
364 if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) { | |
365 SANDBOX_DIE( | |
366 "Trying to start sandbox, even though it is known to be " | |
367 "unavailable"); | |
368 return false; | |
369 } else if (sandbox_has_started_) { | |
370 SANDBOX_DIE( | 113 SANDBOX_DIE( |
371 "Cannot repeatedly start sandbox. Create a separate Sandbox " | 114 "Cannot repeatedly start sandbox. Create a separate Sandbox " |
372 "object instead."); | 115 "object instead."); |
373 return false; | 116 return false; |
374 } | 117 } |
375 if (proc_fd_ < 0) { | |
376 proc_fd_ = open("/proc", O_RDONLY | O_DIRECTORY); | |
377 } | |
378 if (proc_fd_ < 0) { | |
379 // For now, continue in degraded mode, if we can't access /proc. | |
380 // In the future, we might want to tighten this requirement. | |
381 } | |
382 | 118 |
383 bool supports_tsync = | 119 const bool supports_tsync = KernelSupportsSeccompTsync(); |
384 SupportsSeccompThreadFilterSynchronization() == STATUS_AVAILABLE; | |
385 | 120 |
386 if (thread_state == PROCESS_SINGLE_THREADED) { | 121 if (seccomp_level == SeccompLevel::SINGLE_THREADED) { |
387 if (!IsSingleThreaded(proc_fd_)) { | 122 if (!IsSingleThreaded(proc_task_fd_.get())) { |
388 SANDBOX_DIE("Cannot start sandbox; process is already multi-threaded"); | 123 SANDBOX_DIE("Cannot start sandbox; process is already multi-threaded"); |
389 return false; | 124 return false; |
390 } | 125 } |
391 } else if (thread_state == PROCESS_MULTI_THREADED) { | 126 } else if (seccomp_level == SeccompLevel::MULTI_THREADED) { |
392 if (IsSingleThreaded(proc_fd_)) { | 127 if (IsSingleThreaded(proc_task_fd_.get())) { |
393 SANDBOX_DIE("Cannot start sandbox; " | 128 SANDBOX_DIE("Cannot start sandbox; " |
394 "process may be single-threaded when reported as not"); | 129 "process may be single-threaded when reported as not"); |
395 return false; | 130 return false; |
396 } | 131 } |
397 if (!supports_tsync) { | 132 if (!supports_tsync) { |
398 SANDBOX_DIE("Cannot start sandbox; kernel does not support synchronizing " | 133 SANDBOX_DIE("Cannot start sandbox; kernel does not support synchronizing " |
399 "filters for a threadgroup"); | 134 "filters for a threadgroup"); |
400 return false; | 135 return false; |
401 } | 136 } |
402 } | 137 } |
403 | 138 |
404 // We no longer need access to any files in /proc. We want to do this | 139 // We no longer need access to any files in /proc. We want to do this |
405 // before installing the filters, just in case that our policy denies | 140 // before installing the filters, just in case that our policy denies |
406 // close(). | 141 // close(). |
407 if (proc_fd_ >= 0) { | 142 if (proc_task_fd_.is_valid()) { |
408 if (IGNORE_EINTR(close(proc_fd_))) { | 143 proc_task_fd_.reset(); |
409 SANDBOX_DIE("Failed to close file descriptor for /proc"); | |
410 return false; | |
411 } | |
412 proc_fd_ = -1; | |
413 } | 144 } |
414 | 145 |
415 // Install the filters. | 146 // Install the filters. |
416 InstallFilter(supports_tsync || thread_state == PROCESS_MULTI_THREADED); | 147 InstallFilter(supports_tsync || |
417 | 148 seccomp_level == SeccompLevel::MULTI_THREADED); |
418 // We are now inside the sandbox. | |
419 status_ = STATUS_ENABLED; | |
420 | 149 |
421 return true; | 150 return true; |
422 } | 151 } |
423 | 152 |
424 // Don't take a scoped_ptr here, polymorphism make their use awkward. | 153 void SandboxBPF::SetProcTaskFd(base::ScopedFD proc_task_fd) { |
425 void SandboxBPF::SetSandboxPolicy(bpf_dsl::Policy* policy) { | 154 proc_task_fd_.swap(proc_task_fd); |
426 DCHECK(!policy_); | 155 } |
427 if (sandbox_has_started_) { | 156 |
428 SANDBOX_DIE("Cannot change policy after sandbox has started"); | 157 // static |
| 158 bool SandboxBPF::IsValidSyscallNumber(int sysnum) { |
| 159 return SyscallSet::IsValid(sysnum); |
| 160 } |
| 161 |
| 162 // static |
| 163 bool SandboxBPF::IsRequiredForUnsafeTrap(int sysno) { |
| 164 return bpf_dsl::PolicyCompiler::IsRequiredForUnsafeTrap(sysno); |
| 165 } |
| 166 |
| 167 // static |
| 168 intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) { |
| 169 return Syscall::Call( |
| 170 args.nr, static_cast<intptr_t>(args.args[0]), |
| 171 static_cast<intptr_t>(args.args[1]), static_cast<intptr_t>(args.args[2]), |
| 172 static_cast<intptr_t>(args.args[3]), static_cast<intptr_t>(args.args[4]), |
| 173 static_cast<intptr_t>(args.args[5])); |
| 174 } |
| 175 |
| 176 scoped_ptr<CodeGen::Program> SandboxBPF::AssembleFilter( |
| 177 bool force_verification) { |
| 178 #if !defined(NDEBUG) |
| 179 force_verification = true; |
| 180 #endif |
| 181 DCHECK(policy_); |
| 182 bpf_dsl::PolicyCompiler compiler(policy_.get(), Trap::Registry()); |
| 183 scoped_ptr<CodeGen::Program> program = compiler.Compile(); |
| 184 |
| 185 // Make sure compilation resulted in a BPF program that executes |
| 186 // correctly. Otherwise, there is an internal error in our BPF compiler. |
| 187 // There is really nothing the caller can do until the bug is fixed. |
| 188 if (force_verification) { |
| 189 // Verification is expensive. We only perform this step, if we are |
| 190 // compiled in debug mode, or if the caller explicitly requested |
| 191 // verification. |
| 192 |
| 193 const char* err = NULL; |
| 194 if (!Verifier::VerifyBPF(&compiler, *program, *policy_, &err)) { |
| 195 bpf_dsl::DumpBPF::PrintProgram(*program); |
| 196 SANDBOX_DIE(err); |
| 197 } |
429 } | 198 } |
430 policy_.reset(policy); | 199 |
| 200 return program.Pass(); |
431 } | 201 } |
432 | 202 |
433 void SandboxBPF::InstallFilter(bool must_sync_threads) { | 203 void SandboxBPF::InstallFilter(bool must_sync_threads) { |
434 // We want to be very careful in not imposing any requirements on the | 204 // We want to be very careful in not imposing any requirements on the |
435 // policies that are set with SetSandboxPolicy(). This means, as soon as | 205 // policies that are set with SetSandboxPolicy(). This means, as soon as |
436 // the sandbox is active, we shouldn't be relying on libraries that could | 206 // the sandbox is active, we shouldn't be relying on libraries that could |
437 // be making system calls. This, for example, means we should avoid | 207 // be making system calls. This, for example, means we should avoid |
438 // using the heap and we should avoid using STL functions. | 208 // using the heap and we should avoid using STL functions. |
439 // Temporarily copy the contents of the "program" vector into a | 209 // Temporarily copy the contents of the "program" vector into a |
440 // stack-allocated array; and then explicitly destroy that object. | 210 // stack-allocated array; and then explicitly destroy that object. |
441 // This makes sure we don't ex- or implicitly call new/delete after we | 211 // This makes sure we don't ex- or implicitly call new/delete after we |
442 // installed the BPF filter program in the kernel. Depending on the | 212 // installed the BPF filter program in the kernel. Depending on the |
443 // system memory allocator that is in effect, these operators can result | 213 // system memory allocator that is in effect, these operators can result |
444 // in system calls to things like munmap() or brk(). | 214 // in system calls to things like munmap() or brk(). |
445 CodeGen::Program* program = AssembleFilter(false).release(); | 215 CodeGen::Program* program = AssembleFilter(false).release(); |
446 | 216 |
447 struct sock_filter bpf[program->size()]; | 217 struct sock_filter bpf[program->size()]; |
448 const struct sock_fprog prog = {static_cast<unsigned short>(program->size()), | 218 const struct sock_fprog prog = {static_cast<unsigned short>(program->size()), |
449 bpf}; | 219 bpf}; |
450 memcpy(bpf, &(*program)[0], sizeof(bpf)); | 220 memcpy(bpf, &(*program)[0], sizeof(bpf)); |
451 delete program; | 221 delete program; |
452 | 222 |
453 // Make an attempt to release memory that is no longer needed here, rather | 223 // Make an attempt to release memory that is no longer needed here, rather |
454 // than in the destructor. Try to avoid as much as possible to presume of | 224 // than in the destructor. Try to avoid as much as possible to presume of |
455 // what will be possible to do in the new (sandboxed) execution environment. | 225 // what will be possible to do in the new (sandboxed) execution environment. |
456 policy_.reset(); | 226 policy_.reset(); |
457 | 227 |
458 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | 228 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { |
459 SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to enable no-new-privs"); | 229 SANDBOX_DIE("Kernel refuses to enable no-new-privs"); |
460 } | 230 } |
461 | 231 |
462 // Install BPF filter program. If the thread state indicates multi-threading | 232 // Install BPF filter program. If the thread state indicates multi-threading |
463 // support, then the kernel hass the seccomp system call. Otherwise, fall | 233 // support, then the kernel hass the seccomp system call. Otherwise, fall |
464 // back on prctl, which requires the process to be single-threaded. | 234 // back on prctl, which requires the process to be single-threaded. |
465 if (must_sync_threads) { | 235 if (must_sync_threads) { |
466 int rv = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, | 236 int rv = |
467 SECCOMP_FILTER_FLAG_TSYNC, reinterpret_cast<const char*>(&prog)); | 237 sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &prog); |
468 if (rv) { | 238 if (rv) { |
469 SANDBOX_DIE(quiet_ ? NULL : | 239 SANDBOX_DIE( |
470 "Kernel refuses to turn on and synchronize threads for BPF filters"); | 240 "Kernel refuses to turn on and synchronize threads for BPF filters"); |
471 } | 241 } |
472 } else { | 242 } else { |
473 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { | 243 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { |
474 SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to turn on BPF filters"); | 244 SANDBOX_DIE("Kernel refuses to turn on BPF filters"); |
475 } | 245 } |
476 } | 246 } |
477 | 247 |
478 sandbox_has_started_ = true; | 248 sandbox_has_started_ = true; |
479 } | 249 } |
480 | 250 |
481 scoped_ptr<CodeGen::Program> SandboxBPF::AssembleFilter( | |
482 bool force_verification) { | |
483 #if !defined(NDEBUG) | |
484 force_verification = true; | |
485 #endif | |
486 | |
487 bpf_dsl::PolicyCompiler compiler(policy_.get(), Trap::Registry()); | |
488 scoped_ptr<CodeGen::Program> program = compiler.Compile(); | |
489 | |
490 // Make sure compilation resulted in BPF program that executes | |
491 // correctly. Otherwise, there is an internal error in our BPF compiler. | |
492 // There is really nothing the caller can do until the bug is fixed. | |
493 if (force_verification) { | |
494 // Verification is expensive. We only perform this step, if we are | |
495 // compiled in debug mode, or if the caller explicitly requested | |
496 // verification. | |
497 | |
498 const char* err = NULL; | |
499 if (!Verifier::VerifyBPF(&compiler, *program, *policy_, &err)) { | |
500 bpf_dsl::DumpBPF::PrintProgram(*program); | |
501 SANDBOX_DIE(err); | |
502 } | |
503 } | |
504 | |
505 return program.Pass(); | |
506 } | |
507 | |
508 bool SandboxBPF::IsRequiredForUnsafeTrap(int sysno) { | |
509 return bpf_dsl::PolicyCompiler::IsRequiredForUnsafeTrap(sysno); | |
510 } | |
511 | |
512 intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) { | |
513 return Syscall::Call(args.nr, | |
514 static_cast<intptr_t>(args.args[0]), | |
515 static_cast<intptr_t>(args.args[1]), | |
516 static_cast<intptr_t>(args.args[2]), | |
517 static_cast<intptr_t>(args.args[3]), | |
518 static_cast<intptr_t>(args.args[4]), | |
519 static_cast<intptr_t>(args.args[5])); | |
520 } | |
521 | |
522 SandboxBPF::SandboxStatus SandboxBPF::status_ = STATUS_UNKNOWN; | |
523 | |
524 } // namespace sandbox | 251 } // namespace sandbox |
OLD | NEW |