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

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

Issue 733303004: Linux sandbox: change API to start the sandbox (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address nits from Jorge. Created 6 years 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
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>
(...skipping 24 matching lines...) Expand all
35 #include "sandbox/linux/seccomp-bpf/codegen.h" 35 #include "sandbox/linux/seccomp-bpf/codegen.h"
36 #include "sandbox/linux/seccomp-bpf/die.h" 36 #include "sandbox/linux/seccomp-bpf/die.h"
37 #include "sandbox/linux/seccomp-bpf/errorcode.h" 37 #include "sandbox/linux/seccomp-bpf/errorcode.h"
38 #include "sandbox/linux/seccomp-bpf/linux_seccomp.h" 38 #include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
39 #include "sandbox/linux/seccomp-bpf/syscall.h" 39 #include "sandbox/linux/seccomp-bpf/syscall.h"
40 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" 40 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
41 #include "sandbox/linux/seccomp-bpf/trap.h" 41 #include "sandbox/linux/seccomp-bpf/trap.h"
42 #include "sandbox/linux/seccomp-bpf/verifier.h" 42 #include "sandbox/linux/seccomp-bpf/verifier.h"
43 #include "sandbox/linux/services/linux_syscalls.h" 43 #include "sandbox/linux/services/linux_syscalls.h"
44 #include "sandbox/linux/services/syscall_wrappers.h" 44 #include "sandbox/linux/services/syscall_wrappers.h"
45 #include "sandbox/linux/services/thread_helpers.h"
45 46
46 using sandbox::bpf_dsl::Allow; 47 using sandbox::bpf_dsl::Allow;
47 using sandbox::bpf_dsl::Error; 48 using sandbox::bpf_dsl::Error;
48 using sandbox::bpf_dsl::ResultExpr; 49 using sandbox::bpf_dsl::ResultExpr;
49 50
50 namespace sandbox { 51 namespace sandbox {
51 52
52 namespace { 53 namespace {
53 54
54 const int kExpectedExitCode = 100; 55 const int kExpectedExitCode = 100;
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
115 void TryVsyscallProcess(void) { 116 void TryVsyscallProcess(void) {
116 time_t current_time; 117 time_t current_time;
117 // time() is implemented as a vsyscall. With an older glibc, with 118 // time() is implemented as a vsyscall. With an older glibc, with
118 // vsyscall=emulate and some versions of the seccomp BPF patch 119 // vsyscall=emulate and some versions of the seccomp BPF patch
119 // we may get SIGKILL-ed. Detect this! 120 // we may get SIGKILL-ed. Detect this!
120 if (time(&current_time) != static_cast<time_t>(-1)) { 121 if (time(&current_time) != static_cast<time_t>(-1)) {
121 sys_exit_group(kExpectedExitCode); 122 sys_exit_group(kExpectedExitCode);
122 } 123 }
123 } 124 }
124 125
125 bool IsSingleThreaded(int proc_fd) { 126 bool IsSingleThreaded(int proc_task_fd) {
126 if (proc_fd < 0) { 127 return ThreadHelpers::IsSingleThreaded(proc_task_fd);
127 // Cannot determine whether program is single-threaded. Hope for
128 // the best...
129 return true;
130 }
131
132 struct stat sb;
133 int task = -1;
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;
141 }
142 return true;
143 } 128 }
144 129
145 } // namespace 130 } // namespace
146 131
147 SandboxBPF::SandboxBPF() 132 SandboxBPF::SandboxBPF()
148 : quiet_(false), proc_fd_(-1), sandbox_has_started_(false), policy_() { 133 : quiet_(false), proc_task_fd_(-1), sandbox_has_started_(false), policy_() {
149 } 134 }
150 135
151 SandboxBPF::~SandboxBPF() { 136 SandboxBPF::~SandboxBPF() {
137 if (proc_task_fd_ != -1)
138 IGNORE_EINTR(close(proc_task_fd_));
152 } 139 }
153 140
154 bool SandboxBPF::IsValidSyscallNumber(int sysnum) { 141 bool SandboxBPF::IsValidSyscallNumber(int sysnum) {
155 return SyscallSet::IsValid(sysnum); 142 return SyscallSet::IsValid(sysnum);
156 } 143 }
157 144
158 bool SandboxBPF::RunFunctionInPolicy(void (*code_in_sandbox)(), 145 bool SandboxBPF::RunFunctionInPolicy(void (*code_in_sandbox)(),
159 scoped_ptr<bpf_dsl::Policy> policy) { 146 scoped_ptr<bpf_dsl::Policy> policy) {
160 // Block all signals before forking a child process. This prevents an 147 // Block all signals before forking a child process. This prevents an
161 // attacker from manipulating our test by sending us an unexpected signal. 148 // attacker from manipulating our test by sending us an unexpected signal.
162 sigset_t old_mask, new_mask; 149 sigset_t old_mask, new_mask;
163 if (sigfillset(&new_mask) || sigprocmask(SIG_BLOCK, &new_mask, &old_mask)) { 150 if (sigfillset(&new_mask) || sigprocmask(SIG_BLOCK, &new_mask, &old_mask)) {
164 SANDBOX_DIE("sigprocmask() failed"); 151 SANDBOX_DIE("sigprocmask() failed");
165 } 152 }
166 int fds[2]; 153 int fds[2];
167 if (pipe2(fds, O_NONBLOCK | O_CLOEXEC)) { 154 if (pipe2(fds, O_NONBLOCK | O_CLOEXEC)) {
168 SANDBOX_DIE("pipe() failed"); 155 SANDBOX_DIE("pipe() failed");
169 } 156 }
170 157
171 if (fds[0] <= 2 || fds[1] <= 2) { 158 if (fds[0] <= 2 || fds[1] <= 2) {
172 SANDBOX_DIE("Process started without standard file descriptors"); 159 SANDBOX_DIE("Process started without standard file descriptors");
173 } 160 }
174 161
175 // This code is using fork() and should only ever run single-threaded. 162 // 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 163 // Most of the code below is "async-signal-safe" and only minor changes
177 // would be needed to support threads. 164 // would be needed to support threads.
178 DCHECK(IsSingleThreaded(proc_fd_)); 165 DCHECK(IsSingleThreaded(proc_task_fd_));
179 pid_t pid = fork(); 166 pid_t pid = fork();
180 if (pid < 0) { 167 if (pid < 0) {
181 // Die if we cannot fork(). We would probably fail a little later 168 // 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 169 // anyway, as the machine is likely very close to running out of
183 // memory. 170 // memory.
184 // But what we don't want to do is return "false", as a crafty 171 // 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 172 // attacker might cause fork() to fail at will and could trick us
186 // into running without a sandbox. 173 // into running without a sandbox.
187 sigprocmask(SIG_SETMASK, &old_mask, NULL); // OK, if it fails 174 sigprocmask(SIG_SETMASK, &old_mask, NULL); // OK, if it fails
188 SANDBOX_DIE("fork() failed unexpectedly"); 175 SANDBOX_DIE("fork() failed unexpectedly");
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 } 262 }
276 263
277 bool SandboxBPF::KernelSupportSeccompBPF() { 264 bool SandboxBPF::KernelSupportSeccompBPF() {
278 return RunFunctionInPolicy(ProbeProcess, 265 return RunFunctionInPolicy(ProbeProcess,
279 scoped_ptr<bpf_dsl::Policy>(new ProbePolicy())) && 266 scoped_ptr<bpf_dsl::Policy>(new ProbePolicy())) &&
280 RunFunctionInPolicy(TryVsyscallProcess, 267 RunFunctionInPolicy(TryVsyscallProcess,
281 scoped_ptr<bpf_dsl::Policy>(new AllowAllPolicy())); 268 scoped_ptr<bpf_dsl::Policy>(new AllowAllPolicy()));
282 } 269 }
283 270
284 // static 271 // static
285 SandboxBPF::SandboxStatus SandboxBPF::SupportsSeccompSandbox(int proc_fd) { 272 SandboxBPF::SandboxStatus SandboxBPF::SupportsSeccompSandbox() {
286 // It the sandbox is currently active, we clearly must have support for 273 if (status_ != STATUS_UNKNOWN) {
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_; 274 return status_;
311 } 275 }
312 276
313 // If we have not previously checked for availability of the sandbox or if 277 // 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 278 // we otherwise don't believe to have a good cached value, we have to
315 // perform a thorough check now. 279 // 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 280
322 // By setting "quiet_ = true" we suppress messages for expected and benign 281 // We create our own private copy of a "Sandbox" object. This ensures that
323 // failures (e.g. if the current kernel lacks support for BPF filters). 282 // the object does not have any policies configured, that might interfere
324 sandbox.quiet_ = true; 283 // with the tests done by "KernelSupportSeccompBPF()".
325 sandbox.set_proc_fd(proc_fd); 284 SandboxBPF sandbox;
326 status_ = sandbox.KernelSupportSeccompBPF() ? STATUS_AVAILABLE
327 : STATUS_UNSUPPORTED;
328 285
329 // As we are performing our tests from a child process, the run-time 286 // By setting "quiet_ = true" we suppress messages for expected and benign
330 // environment that is visible to the sandbox is always guaranteed to be 287 // failures (e.g. if the current kernel lacks support for BPF filters).
331 // single-threaded. Let's check here whether the caller is single- 288 // TODO(jln): use kernel API to check for seccomp support now that things
332 // threaded. Otherwise, we mark the sandbox as temporarily unavailable. 289 // have stabilized.
333 if (status_ == STATUS_AVAILABLE && !IsSingleThreaded(proc_fd)) { 290 sandbox.quiet_ = true;
334 status_ = STATUS_UNAVAILABLE; 291 status_ =
335 } 292 sandbox.KernelSupportSeccompBPF() ? STATUS_AVAILABLE : STATUS_UNSUPPORTED;
336 } 293
337 return status_; 294 return status_;
338 } 295 }
339 296
340 // static 297 // static
341 SandboxBPF::SandboxStatus 298 SandboxBPF::SandboxStatus
342 SandboxBPF::SupportsSeccompThreadFilterSynchronization() { 299 SandboxBPF::SupportsSeccompThreadFilterSynchronization() {
343 // Applying NO_NEW_PRIVS, a BPF filter, and synchronizing the filter across 300 // Applying NO_NEW_PRIVS, a BPF filter, and synchronizing the filter across
344 // the thread group are all handled atomically by this syscall. 301 // the thread group are all handled atomically by this syscall.
345 const int rv = syscall( 302 const int rv = syscall(
346 __NR_seccomp, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, NULL); 303 __NR_seccomp, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, NULL);
347 304
348 if (rv == -1 && errno == EFAULT) { 305 if (rv == -1 && errno == EFAULT) {
349 return STATUS_AVAILABLE; 306 return STATUS_AVAILABLE;
350 } else { 307 } else {
351 // TODO(jln): turn these into DCHECK after 417888 is considered fixed. 308 // TODO(jln): turn these into DCHECK after 417888 is considered fixed.
352 CHECK_EQ(-1, rv); 309 CHECK_EQ(-1, rv);
353 CHECK(ENOSYS == errno || EINVAL == errno); 310 CHECK(ENOSYS == errno || EINVAL == errno);
354 return STATUS_UNSUPPORTED; 311 return STATUS_UNSUPPORTED;
355 } 312 }
356 } 313 }
357 314
358 void SandboxBPF::set_proc_fd(int proc_fd) { proc_fd_ = proc_fd; } 315 void SandboxBPF::set_proc_task_fd(int proc_task_fd) {
316 proc_task_fd_ = proc_task_fd;
317 }
359 318
360 bool SandboxBPF::StartSandbox(SandboxThreadState thread_state) { 319 bool SandboxBPF::StartSandbox(SandboxThreadState thread_state) {
361 CHECK(thread_state == PROCESS_SINGLE_THREADED || 320 CHECK(thread_state == PROCESS_SINGLE_THREADED ||
362 thread_state == PROCESS_MULTI_THREADED); 321 thread_state == PROCESS_MULTI_THREADED);
363 322
364 if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) { 323 if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) {
365 SANDBOX_DIE( 324 SANDBOX_DIE(
366 "Trying to start sandbox, even though it is known to be " 325 "Trying to start sandbox, even though it is known to be "
367 "unavailable"); 326 "unavailable");
368 return false; 327 return false;
369 } else if (sandbox_has_started_) { 328 } else if (sandbox_has_started_) {
370 SANDBOX_DIE( 329 SANDBOX_DIE(
371 "Cannot repeatedly start sandbox. Create a separate Sandbox " 330 "Cannot repeatedly start sandbox. Create a separate Sandbox "
372 "object instead."); 331 "object instead.");
373 return false; 332 return false;
374 } 333 }
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 334
383 bool supports_tsync = 335 bool supports_tsync =
384 SupportsSeccompThreadFilterSynchronization() == STATUS_AVAILABLE; 336 SupportsSeccompThreadFilterSynchronization() == STATUS_AVAILABLE;
385 337
386 if (thread_state == PROCESS_SINGLE_THREADED) { 338 if (thread_state == PROCESS_SINGLE_THREADED) {
387 if (!IsSingleThreaded(proc_fd_)) { 339 if (!IsSingleThreaded(proc_task_fd_)) {
388 SANDBOX_DIE("Cannot start sandbox; process is already multi-threaded"); 340 SANDBOX_DIE("Cannot start sandbox; process is already multi-threaded");
389 return false; 341 return false;
390 } 342 }
391 } else if (thread_state == PROCESS_MULTI_THREADED) { 343 } else if (thread_state == PROCESS_MULTI_THREADED) {
392 if (IsSingleThreaded(proc_fd_)) { 344 if (IsSingleThreaded(proc_task_fd_)) {
393 SANDBOX_DIE("Cannot start sandbox; " 345 SANDBOX_DIE("Cannot start sandbox; "
394 "process may be single-threaded when reported as not"); 346 "process may be single-threaded when reported as not");
395 return false; 347 return false;
396 } 348 }
397 if (!supports_tsync) { 349 if (!supports_tsync) {
398 SANDBOX_DIE("Cannot start sandbox; kernel does not support synchronizing " 350 SANDBOX_DIE("Cannot start sandbox; kernel does not support synchronizing "
399 "filters for a threadgroup"); 351 "filters for a threadgroup");
400 return false; 352 return false;
401 } 353 }
402 } 354 }
403 355
404 // We no longer need access to any files in /proc. We want to do this 356 // 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 357 // before installing the filters, just in case that our policy denies
406 // close(). 358 // close().
407 if (proc_fd_ >= 0) { 359 if (proc_task_fd_ >= 0) {
408 if (IGNORE_EINTR(close(proc_fd_))) { 360 if (IGNORE_EINTR(close(proc_task_fd_))) {
409 SANDBOX_DIE("Failed to close file descriptor for /proc"); 361 SANDBOX_DIE("Failed to close file descriptor for /proc");
410 return false; 362 return false;
411 } 363 }
412 proc_fd_ = -1; 364 proc_task_fd_ = -1;
413 } 365 }
414 366
415 // Install the filters. 367 // Install the filters.
416 InstallFilter(supports_tsync || thread_state == PROCESS_MULTI_THREADED); 368 InstallFilter(supports_tsync || thread_state == PROCESS_MULTI_THREADED);
417 369
418 // We are now inside the sandbox. 370 // We are now inside the sandbox.
419 status_ = STATUS_ENABLED; 371 status_ = STATUS_ENABLED;
420 372
421 return true; 373 return true;
422 } 374 }
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
515 static_cast<intptr_t>(args.args[1]), 467 static_cast<intptr_t>(args.args[1]),
516 static_cast<intptr_t>(args.args[2]), 468 static_cast<intptr_t>(args.args[2]),
517 static_cast<intptr_t>(args.args[3]), 469 static_cast<intptr_t>(args.args[3]),
518 static_cast<intptr_t>(args.args[4]), 470 static_cast<intptr_t>(args.args[4]),
519 static_cast<intptr_t>(args.args[5])); 471 static_cast<intptr_t>(args.args[5]));
520 } 472 }
521 473
522 SandboxBPF::SandboxStatus SandboxBPF::status_ = STATUS_UNKNOWN; 474 SandboxBPF::SandboxStatus SandboxBPF::status_ = STATUS_UNKNOWN;
523 475
524 } // namespace sandbox 476 } // namespace sandbox
OLDNEW
« no previous file with comments | « sandbox/linux/seccomp-bpf/sandbox_bpf.h ('k') | sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698