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::isArmPrivateSyscall(int sysnum) { |
38 if (signo < static_cast<int>(MIN_SYSCALL) || | 39 #if defined(__arm__) |
39 signo > static_cast<int>(MAX_SYSCALL)) { | 40 return ((sysnum >= static_cast<int>(MIN_PRIVATE_SYSCALL)) && |
| 41 (sysnum <= static_cast<int>(MAX_PRIVATE_SYSCALL))) || |
| 42 ((sysnum >= static_cast<int>(__ARM_NR_cmpxchg)) && |
| 43 (sysnum <= static_cast<int>(MAX_SYSCALL))); |
| 44 #else |
| 45 return false; |
| 46 #endif |
| 47 } |
| 48 |
| 49 ErrorCode Sandbox::allowAllEvaluator(int sysnum) { |
| 50 if (sysnum < static_cast<int>(MIN_SYSCALL) || |
| 51 sysnum > static_cast<int>(MAX_SYSCALL)) { |
40 return ErrorCode(ENOSYS); | 52 return ErrorCode(ENOSYS); |
41 } | 53 } |
| 54 #if defined(__arm__) |
| 55 if (!isArmPrivateSyscall(sysnum)) { |
| 56 return ErrorCode(ENOSYS); |
| 57 } |
| 58 #endif |
42 return ErrorCode(ErrorCode::ERR_ALLOWED); | 59 return ErrorCode(ErrorCode::ERR_ALLOWED); |
43 } | 60 } |
44 | 61 |
45 void Sandbox::tryVsyscallProcess(void) { | 62 void Sandbox::tryVsyscallProcess(void) { |
46 time_t current_time; | 63 time_t current_time; |
47 // time() is implemented as a vsyscall. With an older glibc, with | 64 // time() is implemented as a vsyscall. With an older glibc, with |
48 // vsyscall=emulate and some versions of the seccomp BPF patch | 65 // vsyscall=emulate and some versions of the seccomp BPF patch |
49 // we may get SIGKILL-ed. Detect this! | 66 // we may get SIGKILL-ed. Detect this! |
50 if (time(¤t_time) != static_cast<time_t>(-1)) { | 67 if (time(¤t_time) != static_cast<time_t>(-1)) { |
51 syscall(__NR_exit_group, (intptr_t)100); | 68 syscall(__NR_exit_group, (intptr_t)100); |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
265 } | 282 } |
266 | 283 |
267 bool Sandbox::isDenied(const ErrorCode& code) { | 284 bool Sandbox::isDenied(const ErrorCode& code) { |
268 return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP || | 285 return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP || |
269 (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) && | 286 (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) && |
270 code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO)); | 287 code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO)); |
271 } | 288 } |
272 | 289 |
273 void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator, | 290 void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator, |
274 EvaluateArguments) { | 291 EvaluateArguments) { |
275 // Do some sanity checks on the policy. This will warn users if they do | 292 for (SyscallIterator iter(true); !iter.Done(); ) { |
276 // things that are likely unsafe and unintended. | 293 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))) { | 294 if (!isDenied(syscallEvaluator(sysnum))) { |
289 SANDBOX_DIE("In x32 mode, you should not allow any non-x32 " | 295 SANDBOX_DIE("Policies should deny system calls that are outside the " |
290 "system calls"); | 296 "expected range (typically MIN_SYSCALL..MAX_SYSCALL)"); |
291 } | 297 } |
292 } | 298 } |
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; | 299 return; |
316 } | 300 } |
317 | 301 |
318 void Sandbox::setSandboxPolicy(EvaluateSyscall syscallEvaluator, | 302 void Sandbox::setSandboxPolicy(EvaluateSyscall syscallEvaluator, |
319 EvaluateArguments argumentEvaluator) { | 303 EvaluateArguments argumentEvaluator) { |
320 if (status_ == STATUS_ENABLED) { | 304 if (status_ == STATUS_ENABLED) { |
321 SANDBOX_DIE("Cannot change policy after sandbox has started"); | 305 SANDBOX_DIE("Cannot change policy after sandbox has started"); |
322 } | 306 } |
323 policySanityChecks(syscallEvaluator, argumentEvaluator); | 307 policySanityChecks(syscallEvaluator, argumentEvaluator); |
324 evaluators_.push_back(std::make_pair(syscallEvaluator, argumentEvaluator)); | 308 evaluators_.push_back(std::make_pair(syscallEvaluator, argumentEvaluator)); |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
457 | 441 |
458 void Sandbox::findRanges(Ranges *ranges) { | 442 void Sandbox::findRanges(Ranges *ranges) { |
459 // Please note that "struct seccomp_data" defines system calls as a signed | 443 // Please note that "struct seccomp_data" defines system calls as a signed |
460 // int32_t, but BPF instructions always operate on unsigned quantities. We | 444 // int32_t, but BPF instructions always operate on unsigned quantities. We |
461 // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL, | 445 // 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 | 446 // and then verifying that the rest of the number range (both positive and |
463 // negative) all return the same ErrorCode. | 447 // negative) all return the same ErrorCode. |
464 EvaluateSyscall evaluateSyscall = evaluators_.begin()->first; | 448 EvaluateSyscall evaluateSyscall = evaluators_.begin()->first; |
465 uint32_t oldSysnum = 0; | 449 uint32_t oldSysnum = 0; |
466 ErrorCode oldErr = evaluateSyscall(oldSysnum); | 450 ErrorCode oldErr = evaluateSyscall(oldSysnum); |
467 for (uint32_t sysnum = std::max(1u, MIN_SYSCALL); | 451 ErrorCode invalidErr = evaluateSyscall(MIN_SYSCALL-1); |
468 sysnum <= MAX_SYSCALL + 1; | 452 for (SyscallIterator iter(false); !iter.Done(); ) { |
469 ++sysnum) { | 453 uint32_t sysnum = iter.Next(); |
470 ErrorCode err = evaluateSyscall(static_cast<int>(sysnum)); | 454 ErrorCode err = evaluateSyscall(static_cast<int>(sysnum)); |
471 if (!err.Equals(oldErr)) { | 455 if (!iter.IsValid(sysnum) && !invalidErr.Equals(err)) { |
| 456 // A proper sandbox policy should always treat system calls outside of |
| 457 // the range MIN_SYSCALL..MAX_SYSCALL (i.e. anything that returns |
| 458 // "false" for SyscallIterator::IsValid()) identically. Typically, all |
| 459 // of these system calls would be denied with the same ErrorCode. |
| 460 SANDBOX_DIE("Invalid seccomp policy"); |
| 461 } |
| 462 if (!err.Equals(oldErr) || iter.Done()) { |
472 ranges->push_back(Range(oldSysnum, sysnum-1, oldErr)); | 463 ranges->push_back(Range(oldSysnum, sysnum-1, oldErr)); |
473 oldSysnum = sysnum; | 464 oldSysnum = sysnum; |
474 oldErr = err; | 465 oldErr = err; |
475 } | 466 } |
476 } | 467 } |
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 } | 468 } |
494 | 469 |
495 void Sandbox::emitJumpStatements(Program *program, RetInsns *rets, | 470 void Sandbox::emitJumpStatements(Program *program, RetInsns *rets, |
496 Ranges::const_iterator start, | 471 Ranges::const_iterator start, |
497 Ranges::const_iterator stop) { | 472 Ranges::const_iterator stop) { |
498 // We convert the list of system call ranges into jump table that performs | 473 // We convert the list of system call ranges into jump table that performs |
499 // a binary search over the ranges. | 474 // a binary search over the ranges. |
500 // As a sanity check, we need to have at least two distinct ranges for us | 475 // As a sanity check, we need to have at least two distinct ranges for us |
501 // to be able to build a jump table. | 476 // to be able to build a jump table. |
502 if (stop - start <= 1) { | 477 if (stop - start <= 1) { |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
716 Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN; | 691 Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN; |
717 int Sandbox::proc_fd_ = -1; | 692 int Sandbox::proc_fd_ = -1; |
718 Sandbox::Evaluators Sandbox::evaluators_; | 693 Sandbox::Evaluators Sandbox::evaluators_; |
719 Sandbox::ErrMap Sandbox::errMap_; | 694 Sandbox::ErrMap Sandbox::errMap_; |
720 Sandbox::Traps *Sandbox::traps_ = NULL; | 695 Sandbox::Traps *Sandbox::traps_ = NULL; |
721 Sandbox::TrapIds Sandbox::trapIds_; | 696 Sandbox::TrapIds Sandbox::trapIds_; |
722 ErrorCode *Sandbox::trapArray_ = NULL; | 697 ErrorCode *Sandbox::trapArray_ = NULL; |
723 size_t Sandbox::trapArraySize_ = 0; | 698 size_t Sandbox::trapArraySize_ = 0; |
724 | 699 |
725 } // namespace | 700 } // namespace |
OLD | NEW |