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

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

Issue 11096012: Add a platform-specific syscall number iterator. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Fixed upload. Created 8 years, 2 months 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 <time.h> 5 #include <time.h>
6 6
7 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" 7 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
8 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
8 #include "sandbox/linux/seccomp-bpf/verifier.h" 9 #include "sandbox/linux/seccomp-bpf/verifier.h"
9 10
10 // The kernel gives us a sandbox, we turn it into a playground :-) 11 // The kernel gives us a sandbox, we turn it into a playground :-)
11 // This is version 2 of the playground; version 1 was built on top of 12 // This is version 2 of the playground; version 1 was built on top of
12 // pre-BPF seccomp mode. 13 // pre-BPF seccomp mode.
13 namespace playground2 { 14 namespace playground2 {
14 15
15 // We define a really simple sandbox policy. It is just good enough for us 16 // We define a really simple sandbox policy. It is just good enough for us
16 // to tell that the sandbox has actually been activated. 17 // to tell that the sandbox has actually been activated.
17 ErrorCode Sandbox::probeEvaluator(int signo) { 18 ErrorCode Sandbox::probeEvaluator(int signo) {
18 switch (signo) { 19 switch (signo) {
19 case __NR_getpid: 20 case __NR_getpid:
20 // Return EPERM so that we can check that the filter actually ran. 21 // Return EPERM so that we can check that the filter actually ran.
21 return ErrorCode(EPERM); 22 return ErrorCode(EPERM);
22 case __NR_exit_group: 23 case __NR_exit_group:
23 // Allow exit() with a non-default return code. 24 // Allow exit() with a non-default return code.
24 return ErrorCode(ErrorCode::ERR_ALLOWED); 25 return ErrorCode(ErrorCode::ERR_ALLOWED);
25 default: 26 default:
26 // Make everything else fail in an easily recognizable way. 27 // Make everything else fail in an easily recognizable way.
27 return ErrorCode(EINVAL); 28 return ErrorCode(EINVAL);
28 } 29 }
29 } 30 }
30 31
31 void Sandbox::probeProcess(void) { 32 void Sandbox::probeProcess(void) {
32 if (syscall(__NR_getpid) < 0 && errno == EPERM) { 33 if (syscall(__NR_getpid) < 0 && errno == EPERM) {
33 syscall(__NR_exit_group, (intptr_t)100); 34 syscall(__NR_exit_group, (intptr_t)100);
34 } 35 }
35 } 36 }
36 37
37 ErrorCode Sandbox::allowAllEvaluator(int signo) { 38 bool Sandbox::isValidSyscallNumber(int sysnum) {
38 if (signo < static_cast<int>(MIN_SYSCALL) || 39 return SyscallIterator::IsValid(sysnum);
39 signo > static_cast<int>(MAX_SYSCALL)) { 40 }
41
42 ErrorCode Sandbox::allowAllEvaluator(int sysnum) {
43 if (!isValidSyscallNumber(sysnum)) {
40 return ErrorCode(ENOSYS); 44 return ErrorCode(ENOSYS);
41 } 45 }
42 return ErrorCode(ErrorCode::ERR_ALLOWED); 46 return ErrorCode(ErrorCode::ERR_ALLOWED);
43 } 47 }
44 48
45 void Sandbox::tryVsyscallProcess(void) { 49 void Sandbox::tryVsyscallProcess(void) {
46 time_t current_time; 50 time_t current_time;
47 // time() is implemented as a vsyscall. With an older glibc, with 51 // time() is implemented as a vsyscall. With an older glibc, with
48 // vsyscall=emulate and some versions of the seccomp BPF patch 52 // vsyscall=emulate and some versions of the seccomp BPF patch
49 // we may get SIGKILL-ed. Detect this! 53 // we may get SIGKILL-ed. Detect this!
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
132 } 136 }
133 buf[len] = '\000'; 137 buf[len] = '\000';
134 SANDBOX_DIE(buf); 138 SANDBOX_DIE(buf);
135 } 139 }
136 } 140 }
137 if (HANDLE_EINTR(close(fds[0]))) { 141 if (HANDLE_EINTR(close(fds[0]))) {
138 SANDBOX_DIE("close() failed"); 142 SANDBOX_DIE("close() failed");
139 } 143 }
140 144
141 return rc; 145 return rc;
142
143 } 146 }
144 147
145 bool Sandbox::kernelSupportSeccompBPF(int proc_fd) { 148 bool Sandbox::kernelSupportSeccompBPF(int proc_fd) {
146 #if defined(SECCOMP_BPF_VALGRIND_HACKS) 149 #if defined(SECCOMP_BPF_VALGRIND_HACKS)
147 if (RUNNING_ON_VALGRIND) { 150 if (RUNNING_ON_VALGRIND) {
148 // Valgrind doesn't like our run-time test. Disable testing and assume we 151 // Valgrind doesn't like our run-time test. Disable testing and assume we
149 // always support sandboxing. This feature should only ever be enabled when 152 // always support sandboxing. This feature should only ever be enabled when
150 // debugging. 153 // debugging.
151 return true; 154 return true;
152 } 155 }
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 } 268 }
266 269
267 bool Sandbox::isDenied(const ErrorCode& code) { 270 bool Sandbox::isDenied(const ErrorCode& code) {
268 return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP || 271 return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP ||
269 (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) && 272 (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) &&
270 code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO)); 273 code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO));
271 } 274 }
272 275
273 void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator, 276 void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator,
274 EvaluateArguments) { 277 EvaluateArguments) {
275 // Do some sanity checks on the policy. This will warn users if they do 278 for (SyscallIterator iter(true); !iter.Done(); ) {
276 // things that are likely unsafe and unintended. 279 uint32_t sysnum = iter.Next();
277 // We also have similar checks later, when we actually compile the BPF
278 // program. That catches problems with incorrectly stacked evaluators.
279 if (!isDenied(syscallEvaluator(-1))) {
280 SANDBOX_DIE("Negative system calls should always be disallowed by policy");
281 }
282 #ifndef NDEBUG
283 #if defined(__i386__) || defined(__x86_64__)
284 #if defined(__x86_64__) && defined(__ILP32__)
285 for (unsigned int sysnum = MIN_SYSCALL & ~0x40000000u;
286 sysnum <= (MAX_SYSCALL & ~0x40000000u);
287 ++sysnum) {
288 if (!isDenied(syscallEvaluator(sysnum))) { 280 if (!isDenied(syscallEvaluator(sysnum))) {
289 SANDBOX_DIE("In x32 mode, you should not allow any non-x32 " 281 SANDBOX_DIE("Policies should deny system calls that are outside the "
290 "system calls"); 282 "expected range (typically MIN_SYSCALL..MAX_SYSCALL)");
291 } 283 }
292 } 284 }
293 #else
294 for (unsigned int sysnum = MIN_SYSCALL | 0x40000000u;
295 sysnum <= (MAX_SYSCALL | 0x40000000u);
296 ++sysnum) {
297 if (!isDenied(syscallEvaluator(sysnum))) {
298 SANDBOX_DIE("x32 system calls should be explicitly disallowed");
299 }
300 }
301 #endif
302 #endif
303 #endif
304 // Check interesting boundary values just outside of the valid system call
305 // range: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF, MIN_SYSCALL-1, MAX_SYSCALL+1.
306 // They all should be denied.
307 if (!isDenied(syscallEvaluator(std::numeric_limits<int>::max())) ||
308 !isDenied(syscallEvaluator(std::numeric_limits<int>::min())) ||
309 !isDenied(syscallEvaluator(-1)) ||
310 !isDenied(syscallEvaluator(static_cast<int>(MIN_SYSCALL) - 1)) ||
311 !isDenied(syscallEvaluator(static_cast<int>(MAX_SYSCALL) + 1))) {
312 SANDBOX_DIE("Even for default-allow policies, you must never allow system "
313 "calls outside of the standard system call range");
314 }
315 return; 285 return;
316 } 286 }
317 287
318 void Sandbox::setSandboxPolicy(EvaluateSyscall syscallEvaluator, 288 void Sandbox::setSandboxPolicy(EvaluateSyscall syscallEvaluator,
319 EvaluateArguments argumentEvaluator) { 289 EvaluateArguments argumentEvaluator) {
320 if (status_ == STATUS_ENABLED) { 290 if (status_ == STATUS_ENABLED) {
321 SANDBOX_DIE("Cannot change policy after sandbox has started"); 291 SANDBOX_DIE("Cannot change policy after sandbox has started");
322 } 292 }
323 policySanityChecks(syscallEvaluator, argumentEvaluator); 293 policySanityChecks(syscallEvaluator, argumentEvaluator);
324 evaluators_.push_back(std::make_pair(syscallEvaluator, argumentEvaluator)); 294 evaluators_.push_back(std::make_pair(syscallEvaluator, argumentEvaluator));
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
457 427
458 void Sandbox::findRanges(Ranges *ranges) { 428 void Sandbox::findRanges(Ranges *ranges) {
459 // Please note that "struct seccomp_data" defines system calls as a signed 429 // Please note that "struct seccomp_data" defines system calls as a signed
460 // int32_t, but BPF instructions always operate on unsigned quantities. We 430 // int32_t, but BPF instructions always operate on unsigned quantities. We
461 // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL, 431 // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL,
462 // and then verifying that the rest of the number range (both positive and 432 // and then verifying that the rest of the number range (both positive and
463 // negative) all return the same ErrorCode. 433 // negative) all return the same ErrorCode.
464 EvaluateSyscall evaluateSyscall = evaluators_.begin()->first; 434 EvaluateSyscall evaluateSyscall = evaluators_.begin()->first;
465 uint32_t oldSysnum = 0; 435 uint32_t oldSysnum = 0;
466 ErrorCode oldErr = evaluateSyscall(oldSysnum); 436 ErrorCode oldErr = evaluateSyscall(oldSysnum);
467 for (uint32_t sysnum = std::max(1u, MIN_SYSCALL); 437 ErrorCode invalidErr = evaluateSyscall(MIN_SYSCALL - 1);
468 sysnum <= MAX_SYSCALL + 1; 438 for (SyscallIterator iter(false); !iter.Done(); ) {
469 ++sysnum) { 439 uint32_t sysnum = iter.Next();
470 ErrorCode err = evaluateSyscall(static_cast<int>(sysnum)); 440 ErrorCode err = evaluateSyscall(static_cast<int>(sysnum));
471 if (!err.Equals(oldErr)) { 441 if (!iter.IsValid(sysnum) && !invalidErr.Equals(err)) {
472 ranges->push_back(Range(oldSysnum, sysnum-1, oldErr)); 442 // A proper sandbox policy should always treat system calls outside of
443 // the range MIN_SYSCALL..MAX_SYSCALL (i.e. anything that returns
444 // "false" for SyscallIterator::IsValid()) identically. Typically, all
445 // of these system calls would be denied with the same ErrorCode.
446 SANDBOX_DIE("Invalid seccomp policy");
447 }
448 if (!err.Equals(oldErr) || iter.Done()) {
449 ranges->push_back(Range(oldSysnum, sysnum - 1, oldErr));
473 oldSysnum = sysnum; 450 oldSysnum = sysnum;
474 oldErr = err; 451 oldErr = err;
475 } 452 }
476 } 453 }
477
478 // As we looped all the way past the valid system calls (i.e. MAX_SYSCALL+1),
479 // "oldErr" should at this point be the "default" policy for all system call
480 // numbers that don't have an explicit handler in the system call evaluator.
481 // But as we are quite paranoid, we perform some more sanity checks to verify
482 // that there actually is a consistent "default" policy in the first place.
483 // We don't actually iterate over all possible 2^32 values, though. We just
484 // perform spot checks at the boundaries.
485 // The cases that we test are: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF.
486 if (!oldErr.Equals(evaluateSyscall(std::numeric_limits<int>::max())) ||
487 !oldErr.Equals(evaluateSyscall(std::numeric_limits<int>::min())) ||
488 !oldErr.Equals(evaluateSyscall(-1))) {
489 SANDBOX_DIE("Invalid seccomp policy");
490 }
491 ranges->push_back(
492 Range(oldSysnum, std::numeric_limits<unsigned>::max(), oldErr));
493 } 454 }
494 455
495 void Sandbox::emitJumpStatements(Program *program, RetInsns *rets, 456 void Sandbox::emitJumpStatements(Program *program, RetInsns *rets,
496 Ranges::const_iterator start, 457 Ranges::const_iterator start,
497 Ranges::const_iterator stop) { 458 Ranges::const_iterator stop) {
498 // We convert the list of system call ranges into jump table that performs 459 // We convert the list of system call ranges into jump table that performs
499 // a binary search over the ranges. 460 // a binary search over the ranges.
500 // As a sanity check, we need to have at least two distinct ranges for us 461 // As a sanity check, we need to have at least two distinct ranges for us
501 // to be able to build a jump table. 462 // to be able to build a jump table.
502 if (stop - start <= 1) { 463 if (stop - start <= 1) {
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
716 Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN; 677 Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN;
717 int Sandbox::proc_fd_ = -1; 678 int Sandbox::proc_fd_ = -1;
718 Sandbox::Evaluators Sandbox::evaluators_; 679 Sandbox::Evaluators Sandbox::evaluators_;
719 Sandbox::ErrMap Sandbox::errMap_; 680 Sandbox::ErrMap Sandbox::errMap_;
720 Sandbox::Traps *Sandbox::traps_ = NULL; 681 Sandbox::Traps *Sandbox::traps_ = NULL;
721 Sandbox::TrapIds Sandbox::trapIds_; 682 Sandbox::TrapIds Sandbox::trapIds_;
722 ErrorCode *Sandbox::trapArray_ = NULL; 683 ErrorCode *Sandbox::trapArray_ = NULL;
723 size_t Sandbox::trapArraySize_ = 0; 684 size_t Sandbox::trapArraySize_ = 0;
724 685
725 } // namespace 686 } // namespace
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698