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