| 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 <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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |