Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(409)

Side by Side Diff: sandbox/linux/seccomp-bpf/sandbox_bpf.cc

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

Powered by Google App Engine
This is Rietveld 408576698