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

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: Rename #define's and extract ARM private syscall logic. 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::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(&current_time) != static_cast<time_t>(-1)) { 60 if (time(&current_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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698