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 |