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

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 one comment. 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 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
137 } 141 }
138 buf[len] = '\000'; 142 buf[len] = '\000';
139 SANDBOX_DIE(buf); 143 SANDBOX_DIE(buf);
140 } 144 }
141 } 145 }
142 if (HANDLE_EINTR(close(fds[0]))) { 146 if (HANDLE_EINTR(close(fds[0]))) {
143 SANDBOX_DIE("close() failed"); 147 SANDBOX_DIE("close() failed");
144 } 148 }
145 149
146 return rc; 150 return rc;
147
148 } 151 }
149 152
150 bool Sandbox::kernelSupportSeccompBPF(int proc_fd) { 153 bool Sandbox::kernelSupportSeccompBPF(int proc_fd) {
151 #if defined(SECCOMP_BPF_VALGRIND_HACKS) 154 #if defined(SECCOMP_BPF_VALGRIND_HACKS)
152 if (RUNNING_ON_VALGRIND) { 155 if (RUNNING_ON_VALGRIND) {
153 // Valgrind doesn't like our run-time test. Disable testing and assume we 156 // Valgrind doesn't like our run-time test. Disable testing and assume we
154 // always support sandboxing. This feature should only ever be enabled when 157 // always support sandboxing. This feature should only ever be enabled when
155 // debugging. 158 // debugging.
156 return true; 159 return true;
157 } 160 }
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
270 } 273 }
271 274
272 bool Sandbox::isDenied(const ErrorCode& code) { 275 bool Sandbox::isDenied(const ErrorCode& code) {
273 return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP || 276 return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP ||
274 (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) && 277 (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) &&
275 code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO)); 278 code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO));
276 } 279 }
277 280
278 void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator, 281 void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator,
279 EvaluateArguments) { 282 EvaluateArguments) {
280 // Do some sanity checks on the policy. This will warn users if they do 283 for (SyscallIterator iter(true); !iter.Done(); ) {
281 // things that are likely unsafe and unintended. 284 uint32_t sysnum = iter.Next();
282 // We also have similar checks later, when we actually compile the BPF
283 // program. That catches problems with incorrectly stacked evaluators.
284 if (!isDenied(syscallEvaluator(-1))) {
285 SANDBOX_DIE("Negative system calls should always be disallowed by policy");
286 }
287 #ifndef NDEBUG
288 #if defined(__i386__) || defined(__x86_64__)
289 #if defined(__x86_64__) && defined(__ILP32__)
290 for (unsigned int sysnum = MIN_SYSCALL & ~0x40000000u;
291 sysnum <= (MAX_SYSCALL & ~0x40000000u);
292 ++sysnum) {
293 if (!isDenied(syscallEvaluator(sysnum))) { 285 if (!isDenied(syscallEvaluator(sysnum))) {
294 SANDBOX_DIE("In x32 mode, you should not allow any non-x32 " 286 SANDBOX_DIE("Policies should deny system calls that are outside the "
295 "system calls"); 287 "expected range (typically MIN_SYSCALL..MAX_SYSCALL)");
296 } 288 }
297 } 289 }
298 #else
299 for (unsigned int sysnum = MIN_SYSCALL | 0x40000000u;
300 sysnum <= (MAX_SYSCALL | 0x40000000u);
301 ++sysnum) {
302 if (!isDenied(syscallEvaluator(sysnum))) {
303 SANDBOX_DIE("x32 system calls should be explicitly disallowed");
304 }
305 }
306 #endif
307 #endif
308 #endif
309 // Check interesting boundary values just outside of the valid system call
310 // range: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF, MIN_SYSCALL-1, MAX_SYSCALL+1.
311 // They all should be denied.
312 if (!isDenied(syscallEvaluator(std::numeric_limits<int>::max())) ||
313 !isDenied(syscallEvaluator(std::numeric_limits<int>::min())) ||
314 !isDenied(syscallEvaluator(-1)) ||
315 !isDenied(syscallEvaluator(static_cast<int>(MIN_SYSCALL) - 1)) ||
316 !isDenied(syscallEvaluator(static_cast<int>(MAX_SYSCALL) + 1))) {
317 SANDBOX_DIE("Even for default-allow policies, you must never allow system "
318 "calls outside of the standard system call range");
319 }
320 return; 290 return;
321 } 291 }
322 292
323 void Sandbox::setSandboxPolicy(EvaluateSyscall syscallEvaluator, 293 void Sandbox::setSandboxPolicy(EvaluateSyscall syscallEvaluator,
324 EvaluateArguments argumentEvaluator) { 294 EvaluateArguments argumentEvaluator) {
325 if (status_ == STATUS_ENABLED) { 295 if (status_ == STATUS_ENABLED) {
326 SANDBOX_DIE("Cannot change policy after sandbox has started"); 296 SANDBOX_DIE("Cannot change policy after sandbox has started");
327 } 297 }
328 policySanityChecks(syscallEvaluator, argumentEvaluator); 298 policySanityChecks(syscallEvaluator, argumentEvaluator);
329 evaluators_.push_back(std::make_pair(syscallEvaluator, argumentEvaluator)); 299 evaluators_.push_back(std::make_pair(syscallEvaluator, argumentEvaluator));
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
462 432
463 void Sandbox::findRanges(Ranges *ranges) { 433 void Sandbox::findRanges(Ranges *ranges) {
464 // Please note that "struct seccomp_data" defines system calls as a signed 434 // Please note that "struct seccomp_data" defines system calls as a signed
465 // int32_t, but BPF instructions always operate on unsigned quantities. We 435 // int32_t, but BPF instructions always operate on unsigned quantities. We
466 // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL, 436 // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL,
467 // and then verifying that the rest of the number range (both positive and 437 // and then verifying that the rest of the number range (both positive and
468 // negative) all return the same ErrorCode. 438 // negative) all return the same ErrorCode.
469 EvaluateSyscall evaluateSyscall = evaluators_.begin()->first; 439 EvaluateSyscall evaluateSyscall = evaluators_.begin()->first;
470 uint32_t oldSysnum = 0; 440 uint32_t oldSysnum = 0;
471 ErrorCode oldErr = evaluateSyscall(oldSysnum); 441 ErrorCode oldErr = evaluateSyscall(oldSysnum);
472 for (uint32_t sysnum = std::max(1u, MIN_SYSCALL); 442 ErrorCode invalidErr = evaluateSyscall(MIN_SYSCALL - 1);
473 sysnum <= MAX_SYSCALL + 1; 443 for (SyscallIterator iter(false); !iter.Done(); ) {
474 ++sysnum) { 444 uint32_t sysnum = iter.Next();
475 ErrorCode err = evaluateSyscall(static_cast<int>(sysnum)); 445 ErrorCode err = evaluateSyscall(static_cast<int>(sysnum));
476 if (!err.Equals(oldErr)) { 446 if (!iter.IsValid(sysnum) && !invalidErr.Equals(err)) {
477 ranges->push_back(Range(oldSysnum, sysnum-1, oldErr)); 447 // A proper sandbox policy should always treat system calls outside of
448 // the range MIN_SYSCALL..MAX_SYSCALL (i.e. anything that returns
449 // "false" for SyscallIterator::IsValid()) identically. Typically, all
450 // of these system calls would be denied with the same ErrorCode.
451 SANDBOX_DIE("Invalid seccomp policy");
452 }
453 if (!err.Equals(oldErr) || iter.Done()) {
454 ranges->push_back(Range(oldSysnum, sysnum - 1, oldErr));
478 oldSysnum = sysnum; 455 oldSysnum = sysnum;
479 oldErr = err; 456 oldErr = err;
480 } 457 }
481 } 458 }
482
483 // As we looped all the way past the valid system calls (i.e. MAX_SYSCALL+1),
484 // "oldErr" should at this point be the "default" policy for all system call
485 // numbers that don't have an explicit handler in the system call evaluator.
486 // But as we are quite paranoid, we perform some more sanity checks to verify
487 // that there actually is a consistent "default" policy in the first place.
488 // We don't actually iterate over all possible 2^32 values, though. We just
489 // perform spot checks at the boundaries.
490 // The cases that we test are: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF.
491 if (!oldErr.Equals(evaluateSyscall(std::numeric_limits<int>::max())) ||
492 !oldErr.Equals(evaluateSyscall(std::numeric_limits<int>::min())) ||
493 !oldErr.Equals(evaluateSyscall(-1))) {
494 SANDBOX_DIE("Invalid seccomp policy");
495 }
496 ranges->push_back(
497 Range(oldSysnum, std::numeric_limits<unsigned>::max(), oldErr));
498 } 459 }
499 460
500 void Sandbox::emitJumpStatements(Program *program, RetInsns *rets, 461 void Sandbox::emitJumpStatements(Program *program, RetInsns *rets,
501 Ranges::const_iterator start, 462 Ranges::const_iterator start,
502 Ranges::const_iterator stop) { 463 Ranges::const_iterator stop) {
503 // We convert the list of system call ranges into jump table that performs 464 // We convert the list of system call ranges into jump table that performs
504 // a binary search over the ranges. 465 // a binary search over the ranges.
505 // As a sanity check, we need to have at least two distinct ranges for us 466 // As a sanity check, we need to have at least two distinct ranges for us
506 // to be able to build a jump table. 467 // to be able to build a jump table.
507 if (stop - start <= 1) { 468 if (stop - start <= 1) {
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
721 Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN; 682 Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN;
722 int Sandbox::proc_fd_ = -1; 683 int Sandbox::proc_fd_ = -1;
723 Sandbox::Evaluators Sandbox::evaluators_; 684 Sandbox::Evaluators Sandbox::evaluators_;
724 Sandbox::ErrMap Sandbox::errMap_; 685 Sandbox::ErrMap Sandbox::errMap_;
725 Sandbox::Traps *Sandbox::traps_ = NULL; 686 Sandbox::Traps *Sandbox::traps_ = NULL;
726 Sandbox::TrapIds Sandbox::trapIds_; 687 Sandbox::TrapIds Sandbox::trapIds_;
727 ErrorCode *Sandbox::trapArray_ = NULL; 688 ErrorCode *Sandbox::trapArray_ = NULL;
728 size_t Sandbox::trapArraySize_ = 0; 689 size_t Sandbox::trapArraySize_ = 0;
729 690
730 } // namespace 691 } // namespace
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698