summaryrefslogtreecommitdiffstats
path: root/sandbox/linux
diff options
context:
space:
mode:
authorjorgelo@chromium.org <jorgelo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-15 20:33:16 +0000
committerjorgelo@chromium.org <jorgelo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-15 20:33:16 +0000
commitdb30283ec8bbf3f36adf072c4e5827693a9fde9f (patch)
tree1faaa19d22425b3a6bf87d89e73a98db99c512c2 /sandbox/linux
parentff297228aab73ef4748c650d4ad77b84aeac4425 (diff)
downloadchromium_src-db30283ec8bbf3f36adf072c4e5827693a9fde9f.zip
chromium_src-db30283ec8bbf3f36adf072c4e5827693a9fde9f.tar.gz
chromium_src-db30283ec8bbf3f36adf072c4e5827693a9fde9f.tar.bz2
Add a platform-specific syscall number iterator.
Avoid needlessly expensive scanning of system call ranges. This CL improves how we deal with discontiguous ranges of system call numbers. (Original CL by markus@chromium.org) TEST=sandbox_linux_unittests on x86_64 and ARM BUG=148856 Review URL: https://chromiumcodereview.appspot.com/11096012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@161943 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox/linux')
-rw-r--r--sandbox/linux/sandbox_linux.gypi3
-rw-r--r--sandbox/linux/seccomp-bpf/Makefile2
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf.cc85
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf.h43
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc74
-rw-r--r--sandbox/linux/seccomp-bpf/syscall_iterator.cc91
-rw-r--r--sandbox/linux/seccomp-bpf/syscall_iterator.h58
-rw-r--r--sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc135
-rw-r--r--sandbox/linux/seccomp-bpf/verifier.cc17
9 files changed, 401 insertions, 107 deletions
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi
index 64b8b91..0508f70 100644
--- a/sandbox/linux/sandbox_linux.gypi
+++ b/sandbox/linux/sandbox_linux.gypi
@@ -57,6 +57,7 @@
'seccomp-bpf/bpf_tests.h',
'seccomp-bpf/errorcode_unittest.cc',
'seccomp-bpf/sandbox_bpf_unittest.cc',
+ 'seccomp-bpf/syscall_iterator_unittest.cc',
],
}],
],
@@ -71,6 +72,8 @@
'seccomp-bpf/errorcode.h',
'seccomp-bpf/sandbox_bpf.cc',
'seccomp-bpf/sandbox_bpf.h',
+ 'seccomp-bpf/syscall_iterator.cc',
+ 'seccomp-bpf/syscall_iterator.h',
'seccomp-bpf/verifier.cc',
'seccomp-bpf/verifier.h',
],
diff --git a/sandbox/linux/seccomp-bpf/Makefile b/sandbox/linux/seccomp-bpf/Makefile
index a8a2ca3..920b754 100644
--- a/sandbox/linux/seccomp-bpf/Makefile
+++ b/sandbox/linux/seccomp-bpf/Makefile
@@ -2,7 +2,7 @@ DEF_CFLAGS = -g -O3 -Wall -Werror -Wextra -Wno-missing-field-initializers -fPIC
DEF_CPPFLAGS = -D_GNU_SOURCE -DSECCOMP_BPF_STANDALONE -DSECCOMP_BPF_VALGRIND_HACKS -include valgrind/valgrind.h -iquote ../../..
DEF_LDFLAGS = -g -lpthread
DEPFLAGS = -MMD -MF .$@.d
-MODS := demo sandbox_bpf die errorcode util verifier
+MODS := demo sandbox_bpf die errorcode syscall_iterator util verifier
OBJS64 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o64/')
OBJS32 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o32/')
ALL_OBJS = $(OBJS32) $(OBJS64)
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
index bc7e682..643eacb 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -5,6 +5,7 @@
#include <time.h>
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
#include "sandbox/linux/seccomp-bpf/verifier.h"
// The kernel gives us a sandbox, we turn it into a playground :-)
@@ -34,9 +35,12 @@ void Sandbox::probeProcess(void) {
}
}
-ErrorCode Sandbox::allowAllEvaluator(int signo) {
- if (signo < static_cast<int>(MIN_SYSCALL) ||
- signo > static_cast<int>(MAX_SYSCALL)) {
+bool Sandbox::isValidSyscallNumber(int sysnum) {
+ return SyscallIterator::IsValid(sysnum);
+}
+
+ErrorCode Sandbox::allowAllEvaluator(int sysnum) {
+ if (!isValidSyscallNumber(sysnum)) {
return ErrorCode(ENOSYS);
}
return ErrorCode(ErrorCode::ERR_ALLOWED);
@@ -144,7 +148,6 @@ bool Sandbox::RunFunctionInPolicy(void (*CodeInSandbox)(),
}
return rc;
-
}
bool Sandbox::kernelSupportSeccompBPF(int proc_fd) {
@@ -277,46 +280,13 @@ bool Sandbox::isDenied(const ErrorCode& code) {
void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator,
EvaluateArguments) {
- // Do some sanity checks on the policy. This will warn users if they do
- // things that are likely unsafe and unintended.
- // We also have similar checks later, when we actually compile the BPF
- // program. That catches problems with incorrectly stacked evaluators.
- if (!isDenied(syscallEvaluator(-1))) {
- SANDBOX_DIE("Negative system calls should always be disallowed by policy");
- }
-#ifndef NDEBUG
-#if defined(__i386__) || defined(__x86_64__)
-#if defined(__x86_64__) && defined(__ILP32__)
- for (unsigned int sysnum = MIN_SYSCALL & ~0x40000000u;
- sysnum <= (MAX_SYSCALL & ~0x40000000u);
- ++sysnum) {
+ for (SyscallIterator iter(true); !iter.Done(); ) {
+ uint32_t sysnum = iter.Next();
if (!isDenied(syscallEvaluator(sysnum))) {
- SANDBOX_DIE("In x32 mode, you should not allow any non-x32 "
- "system calls");
+ SANDBOX_DIE("Policies should deny system calls that are outside the "
+ "expected range (typically MIN_SYSCALL..MAX_SYSCALL)");
}
}
-#else
- for (unsigned int sysnum = MIN_SYSCALL | 0x40000000u;
- sysnum <= (MAX_SYSCALL | 0x40000000u);
- ++sysnum) {
- if (!isDenied(syscallEvaluator(sysnum))) {
- SANDBOX_DIE("x32 system calls should be explicitly disallowed");
- }
- }
-#endif
-#endif
-#endif
- // Check interesting boundary values just outside of the valid system call
- // range: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF, MIN_SYSCALL-1, MAX_SYSCALL+1.
- // They all should be denied.
- if (!isDenied(syscallEvaluator(std::numeric_limits<int>::max())) ||
- !isDenied(syscallEvaluator(std::numeric_limits<int>::min())) ||
- !isDenied(syscallEvaluator(-1)) ||
- !isDenied(syscallEvaluator(static_cast<int>(MIN_SYSCALL) - 1)) ||
- !isDenied(syscallEvaluator(static_cast<int>(MAX_SYSCALL) + 1))) {
- SANDBOX_DIE("Even for default-allow policies, you must never allow system "
- "calls outside of the standard system call range");
- }
return;
}
@@ -469,32 +439,23 @@ void Sandbox::findRanges(Ranges *ranges) {
EvaluateSyscall evaluateSyscall = evaluators_.begin()->first;
uint32_t oldSysnum = 0;
ErrorCode oldErr = evaluateSyscall(oldSysnum);
- for (uint32_t sysnum = std::max(1u, MIN_SYSCALL);
- sysnum <= MAX_SYSCALL + 1;
- ++sysnum) {
+ ErrorCode invalidErr = evaluateSyscall(MIN_SYSCALL - 1);
+ for (SyscallIterator iter(false); !iter.Done(); ) {
+ uint32_t sysnum = iter.Next();
ErrorCode err = evaluateSyscall(static_cast<int>(sysnum));
- if (!err.Equals(oldErr)) {
- ranges->push_back(Range(oldSysnum, sysnum-1, oldErr));
+ if (!iter.IsValid(sysnum) && !invalidErr.Equals(err)) {
+ // A proper sandbox policy should always treat system calls outside of
+ // the range MIN_SYSCALL..MAX_SYSCALL (i.e. anything that returns
+ // "false" for SyscallIterator::IsValid()) identically. Typically, all
+ // of these system calls would be denied with the same ErrorCode.
+ SANDBOX_DIE("Invalid seccomp policy");
+ }
+ if (!err.Equals(oldErr) || iter.Done()) {
+ ranges->push_back(Range(oldSysnum, sysnum - 1, oldErr));
oldSysnum = sysnum;
oldErr = err;
}
}
-
- // As we looped all the way past the valid system calls (i.e. MAX_SYSCALL+1),
- // "oldErr" should at this point be the "default" policy for all system call
- // numbers that don't have an explicit handler in the system call evaluator.
- // But as we are quite paranoid, we perform some more sanity checks to verify
- // that there actually is a consistent "default" policy in the first place.
- // We don't actually iterate over all possible 2^32 values, though. We just
- // perform spot checks at the boundaries.
- // The cases that we test are: 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF.
- if (!oldErr.Equals(evaluateSyscall(std::numeric_limits<int>::max())) ||
- !oldErr.Equals(evaluateSyscall(std::numeric_limits<int>::min())) ||
- !oldErr.Equals(evaluateSyscall(-1))) {
- SANDBOX_DIE("Invalid seccomp policy");
- }
- ranges->push_back(
- Range(oldSysnum, std::numeric_limits<unsigned>::max(), oldErr));
}
void Sandbox::emitJumpStatements(Program *program, RetInsns *rets,
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
index 5a177ad..8cc3b7b 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.h
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -87,8 +87,9 @@
#define SECCOMP_MAX_PROGRAM_SIZE (1<<30)
#if defined(__i386__)
-#define MIN_SYSCALL 0u
-#define MAX_SYSCALL 1024u
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL 1024u
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
#define SECCOMP_ARCH AUDIT_ARCH_I386
#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
@@ -103,8 +104,9 @@
#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_EBP)
#elif defined(__x86_64__)
-#define MIN_SYSCALL 0u
-#define MAX_SYSCALL 1024u
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL 1024u
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
#define SECCOMP_ARCH AUDIT_ARCH_X86_64
#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
@@ -123,8 +125,12 @@
// and a "ghost syscall private to the kernel", cmpxchg,
// at |__ARM_NR_BASE+0x00fff0|.
// See </arch/arm/include/asm/unistd.h> in the Linux kernel.
-#define MIN_SYSCALL ((unsigned int)__NR_SYSCALL_BASE)
-#define MAX_SYSCALL ((unsigned int)__ARM_NR_BASE + 0x00ffffu)
+#define MIN_SYSCALL ((unsigned int)__NR_SYSCALL_BASE)
+#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + 1024u)
+#define MIN_PRIVATE_SYSCALL ((unsigned int)__ARM_NR_BASE)
+#define MAX_PRIVATE_SYSCALL (MIN_PRIVATE_SYSCALL + 16u)
+#define MIN_GHOST_SYSCALL ((unsigned int)__ARM_NR_BASE + 0xfff0u)
+#define MAX_SYSCALL (MIN_GHOST_SYSCALL + 4u)
// <linux/audit.h> includes <linux/elf-em.h>, which does not define EM_ARM.
// <linux/elf.h> only includes <asm/elf.h> if we're in the kernel.
# if !defined(EM_ARM)
@@ -151,6 +157,15 @@
#endif
+#if defined(SECCOMP_BPF_STANDALONE)
+#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+#define HANDLE_EINTR TEMP_FAILURE_RETRY
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+#endif
+
#include "sandbox/linux/seccomp-bpf/die.h"
#include "sandbox/linux/seccomp-bpf/errorcode.h"
@@ -169,15 +184,6 @@ struct arch_sigsys {
unsigned int arch;
};
-#if defined(SECCOMP_BPF_STANDALONE)
-#define arraysize(x) sizeof(x)/sizeof(*(x)))
-#define HANDLE_EINTR TEMP_FAILURE_RETRY
-#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
- TypeName(); \
- TypeName(const TypeName&); \
- void operator=(const TypeName&)
-#endif
-
class Sandbox {
public:
enum SandboxStatus {
@@ -217,6 +223,11 @@ class Sandbox {
Constraint *constraint);
typedef std::vector<std::pair<EvaluateSyscall,EvaluateArguments> >Evaluators;
+ // Checks whether a particular system call number is valid on the current
+ // architecture. E.g. on ARM there's a non-contiguous range of private
+ // system calls.
+ static bool isValidSyscallNumber(int sysnum);
+
// There are a lot of reasons why the Seccomp sandbox might not be available.
// This could be because the kernel does not support Seccomp mode, or it
// could be because another sandbox is already active.
@@ -291,7 +302,7 @@ class Sandbox {
static ErrorCode probeEvaluator(int signo) __attribute__((const));
static void probeProcess(void);
- static ErrorCode allowAllEvaluator(int signo);
+ static ErrorCode allowAllEvaluator(int sysnum);
static void tryVsyscallProcess(void);
static bool kernelSupportSeccompBPF(int proc_fd);
static bool RunFunctionInPolicy(void (*function)(),
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
index 1422b59..60554b3 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
@@ -13,9 +13,6 @@ using namespace playground2;
namespace {
const int kExpectedReturnValue = 42;
-#if defined(__arm__)
-const int kArmPublicSysnoCeiling = __NR_SYSCALL_BASE + 1024;
-#endif
// This test should execute no matter whether we have kernel support. So,
// we make it a TEST() instead of a BPF_TEST().
@@ -41,11 +38,11 @@ SANDBOX_TEST(SandboxBpf, CallSupportsTwice) {
// A simple blacklist test
ErrorCode BlacklistNanosleepPolicy(int sysno) {
- if (sysno < static_cast<int>(MIN_SYSCALL) ||
- sysno > static_cast<int>(MAX_SYSCALL)) {
+ if (!Sandbox::isValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
}
+
switch (sysno) {
case __NR_nanosleep:
return ErrorCode(EACCES);
@@ -100,11 +97,11 @@ intptr_t EnomemHandler(const struct arch_seccomp_data& args, void *aux) {
}
ErrorCode BlacklistNanosleepPolicySigsys(int sysno) {
- if (sysno < static_cast<int>(MIN_SYSCALL) ||
- sysno > static_cast<int>(MAX_SYSCALL)) {
+ if (!Sandbox::isValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
}
+
switch (sysno) {
case __NR_nanosleep:
return Sandbox::Trap(EnomemHandler,
@@ -148,16 +145,16 @@ int SysnoToRandomErrno(int sysno) {
}
ErrorCode SyntheticPolicy(int sysno) {
- if (sysno < static_cast<int>(MIN_SYSCALL) ||
- sysno > static_cast<int>(MAX_SYSCALL)) {
- // FIXME: we should really not have to do that in a trivial policy.
+ if (!Sandbox::isValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
}
- // TODO(jorgelo): remove this restriction once crbug.com/141694 is fixed.
+// TODO(jorgelo): remove this once the new code generator lands.
#if defined(__arm__)
- if (sysno > kArmPublicSysnoCeiling)
+ if (sysno > static_cast<int>(MAX_PUBLIC_SYSCALL)) {
return ErrorCode(ENOSYS);
+ }
#endif
// TODO(markus): allow calls to write(). This should start working as soon
@@ -177,17 +174,10 @@ BPF_TEST(SandboxBpf, SyntheticPolicy, SyntheticPolicy) {
// overflow.
BPF_ASSERT(
std::numeric_limits<int>::max() - kExpectedReturnValue - 1 >=
- static_cast<int>(MAX_SYSCALL));
-
- // TODO(jorgelo): remove this limit once crbug.com/141694 is fixed.
-#if defined(__arm__)
- const int sysno_ceiling = kArmPublicSysnoCeiling;
-#else
- const int sysno_ceiling = static_cast<int>(MAX_SYSCALL);
-#endif
+ static_cast<int>(MAX_PUBLIC_SYSCALL));
for (int syscall_number = static_cast<int>(MIN_SYSCALL);
- syscall_number <= sysno_ceiling;
+ syscall_number <= static_cast<int>(MAX_PUBLIC_SYSCALL);
++syscall_number) {
if (syscall_number == __NR_exit_group ||
syscall_number == __NR_write) {
@@ -200,4 +190,46 @@ BPF_TEST(SandboxBpf, SyntheticPolicy, SyntheticPolicy) {
}
}
+#if defined(__arm__)
+// A simple policy that tests whether ARM private system calls are supported
+// by our BPF compiler and by the BPF interpreter in the kernel.
+
+// For ARM private system calls, return an errno equal to their offset from
+// MIN_PRIVATE_SYSCALL plus 1 (to avoid NUL errno).
+int ArmPrivateSysnoToErrno(int sysno) {
+ if (sysno >= static_cast<int>(MIN_PRIVATE_SYSCALL) &&
+ sysno <= static_cast<int>(MAX_PRIVATE_SYSCALL)) {
+ return (sysno - MIN_PRIVATE_SYSCALL) + 1;
+ } else {
+ return ENOSYS;
+ }
+}
+
+ErrorCode ArmPrivatePolicy(int sysno) {
+ if (!Sandbox::isValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy.
+ return ErrorCode(ENOSYS);
+ }
+
+ // Start from |__ARM_NR_set_tls + 1| so as not to mess with actual
+ // ARM private system calls.
+ if (sysno >= static_cast<int>(__ARM_NR_set_tls + 1) &&
+ sysno <= static_cast<int>(MAX_PRIVATE_SYSCALL)) {
+ return ErrorCode(ArmPrivateSysnoToErrno(sysno));
+ } else {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+BPF_TEST(SandboxBpf, ArmPrivatePolicy, ArmPrivatePolicy) {
+ for (int syscall_number = static_cast<int>(__ARM_NR_set_tls + 1);
+ syscall_number <= static_cast<int>(MAX_PRIVATE_SYSCALL);
+ ++syscall_number) {
+ errno = 0;
+ BPF_ASSERT(syscall(syscall_number) == -1);
+ BPF_ASSERT(errno == ArmPrivateSysnoToErrno(syscall_number));
+ }
+}
+#endif // defined(__arm__)
+
} // namespace
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator.cc b/sandbox/linux/seccomp-bpf/syscall_iterator.cc
new file mode 100644
index 0000000..583dcf6
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
+
+namespace playground2 {
+
+uint32_t SyscallIterator::Next() {
+ if (done_) {
+ return num_;
+ }
+
+ uint32_t val;
+ do {
+ // |num_| has been initialized to 0, which we assume is also MIN_SYSCALL.
+ // This true for supported architectures (Intel and ARM EABI).
+ CHECK_EQ(MIN_SYSCALL, 0u);
+ val = num_;
+
+ // First we iterate up to MAX_PUBLIC_SYSCALL, which is equal to MAX_SYSCALL
+ // on Intel architectures, but leaves room for private syscalls on ARM.
+ if (num_ <= MAX_PUBLIC_SYSCALL) {
+ if (invalid_only_ && num_ < MAX_PUBLIC_SYSCALL) {
+ num_ = MAX_PUBLIC_SYSCALL;
+ } else {
+ ++num_;
+ }
+#if defined(__arm__)
+ // ARM EABI includes "ARM private" system calls starting at
+ // MIN_PRIVATE_SYSCALL, and a "ghost syscall private to the kernel" at
+ // MIN_GHOST_SYSCALL.
+ } else if (num_ < MIN_PRIVATE_SYSCALL - 1) {
+ num_ = MIN_PRIVATE_SYSCALL - 1;
+ } else if (num_ <= MAX_PRIVATE_SYSCALL) {
+ if (invalid_only_ && num_ < MAX_PRIVATE_SYSCALL) {
+ num_ = MAX_PRIVATE_SYSCALL;
+ } else {
+ ++num_;
+ }
+ } else if (num_ < MIN_GHOST_SYSCALL - 1) {
+ num_ = MIN_GHOST_SYSCALL - 1;
+ } else if (num_ <= MAX_SYSCALL) {
+ if (invalid_only_ && num_ < MAX_SYSCALL) {
+ num_ = MAX_SYSCALL;
+ } else {
+ ++num_;
+ }
+#endif
+ // BPF programs only ever operate on unsigned quantities. So, that's how
+ // we iterate; we return values from 0..0xFFFFFFFFu. But there are places,
+ // where the kernel might interpret system call numbers as signed
+ // quantities, so the boundaries between signed and unsigned values are
+ // potential problem cases. We want to explicitly return these values from
+ // our iterator.
+ } else if (num_ < 0x7FFFFFFFu) {
+ num_ = 0x7FFFFFFFu;
+ } else if (num_ < 0x80000000u) {
+ num_ = 0x80000000u;
+ } else if (num_ < 0xFFFFFFFFu) {
+ num_ = 0xFFFFFFFFu;
+ }
+ } while (invalid_only_ && IsValid(val));
+
+ done_ |= val == 0xFFFFFFFFu;
+ return val;
+}
+
+bool SyscallIterator::IsValid(uint32_t num) {
+ uint32_t min_syscall = MIN_SYSCALL;
+ if (num >= min_syscall && num <= MAX_PUBLIC_SYSCALL) {
+ return true;
+ }
+ if (IsArmPrivate(num)) {
+ return true;
+ }
+ return false;
+}
+
+bool SyscallIterator::IsArmPrivate(uint32_t num) {
+#if defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))
+ return (num >= MIN_PRIVATE_SYSCALL && num <= MAX_PRIVATE_SYSCALL) ||
+ (num >= MIN_GHOST_SYSCALL && num <= MAX_SYSCALL);
+#else
+ return false;
+#endif
+}
+
+} // namespace
+
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator.h b/sandbox/linux/seccomp-bpf/syscall_iterator.h
new file mode 100644
index 0000000..39568d8
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__
+
+#include <stdint.h>
+
+#include <base/logging.h>
+
+namespace playground2 {
+
+// Iterates over the entire system call range from 0..0xFFFFFFFFu. This
+// iterator is aware of how system calls look like and will skip quickly
+// over ranges that can't contain system calls. It iterates more slowly
+// whenever it reaches a range that is potentially problematic, returning
+// the last invalid value before a valid range of system calls, and the
+// first invalid value after a valid range of syscalls. It iterates over
+// individual values whenever it is in the normal range for system calls
+// (typically MIN_SYSCALL..MAX_SYSCALL).
+// If |invalid_only| is true, this iterator will only return invalid
+// syscall numbers, but will still skip quickly over invalid ranges,
+// returning the first invalid value in the range and then skipping
+// to the last invalid value in the range.
+//
+// Example usage:
+// for (SyscallIterator iter(false); !iter.Done(); ) {
+// uint32_t sysnum = iter.Next();
+// // Do something with sysnum.
+// }
+//
+// TODO(markus): Make this a classic C++ iterator.
+class SyscallIterator {
+ public:
+ explicit SyscallIterator(bool invalid_only)
+ : invalid_only_(invalid_only),
+ done_(false),
+ num_(0) {}
+
+ bool Done() const { return done_; }
+ uint32_t Next();
+ static bool IsValid(uint32_t num);
+
+ private:
+ static bool IsArmPrivate(uint32_t num);
+
+ bool invalid_only_;
+ bool done_;
+ uint32_t num_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyscallIterator);
+};
+
+} // namespace playground2
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__
+
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc b/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
new file mode 100644
index 0000000..26f11ce
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+using namespace playground2;
+
+namespace {
+
+SANDBOX_TEST(SyscallIterator, Monotonous) {
+ for (int i = 0; i < 2; ++i) {
+ bool invalid_only = !i; // Testing both |invalid_only| cases.
+ SyscallIterator iter(invalid_only);
+ uint32_t next = iter.Next();
+
+ if (!invalid_only) {
+ // The iterator should start at 0.
+ SANDBOX_ASSERT(next == 0);
+ }
+ for (uint32_t last = next; !iter.Done(); last = next) {
+ next = iter.Next();
+ SANDBOX_ASSERT(last < next);
+ }
+ // The iterator should always return 0xFFFFFFFFu as the last value.
+ SANDBOX_ASSERT(next == 0xFFFFFFFFu);
+ }
+}
+
+SANDBOX_TEST(SyscallIterator, PublicSyscallRange) {
+ SyscallIterator iter(false);
+ uint32_t next = iter.Next();
+
+ // The iterator should cover the public syscall range
+ // MIN_SYSCALL..MAX_PUBLIC_SYSCALL, without skipping syscalls.
+ // We're assuming MIN_SYSCALL == 0 for all architectures,
+ // this is currently valid for Intel and ARM EABI.
+ SANDBOX_ASSERT(MIN_SYSCALL == 0);
+ SANDBOX_ASSERT(next == MIN_SYSCALL);
+ for (uint32_t last = next; next < MAX_PUBLIC_SYSCALL + 1; last = next) {
+ SANDBOX_ASSERT((next = iter.Next()) == last + 1);
+ }
+ SANDBOX_ASSERT(next == MAX_PUBLIC_SYSCALL + 1);
+}
+
+#if defined(__arm__)
+SANDBOX_TEST(SyscallIterator, ARMPrivateSyscallRange) {
+ SyscallIterator iter(false);
+ uint32_t next = iter.Next();
+ while (next < MIN_PRIVATE_SYSCALL - 1) {
+ next = iter.Next();
+ }
+ // The iterator should cover the ARM private syscall range
+ // without skipping syscalls.
+ SANDBOX_ASSERT(next == MIN_PRIVATE_SYSCALL - 1);
+ for (uint32_t last = next; next < MAX_PRIVATE_SYSCALL + 1; last = next) {
+ SANDBOX_ASSERT((next = iter.Next()) == last + 1);
+ }
+ SANDBOX_ASSERT(next == MAX_PRIVATE_SYSCALL + 1);
+}
+
+SANDBOX_TEST(SyscallIterator, ARMHiddenSyscallRange) {
+ SyscallIterator iter(false);
+ uint32_t next = iter.Next();
+ while (next < MIN_GHOST_SYSCALL - 1) {
+ next = iter.Next();
+ }
+ // The iterator should cover the ARM hidden syscall range
+ // without skipping syscalls.
+ SANDBOX_ASSERT(next == MIN_GHOST_SYSCALL - 1);
+ for (uint32_t last = next; next < MAX_SYSCALL + 1; last = next) {
+ SANDBOX_ASSERT((next = iter.Next()) == last + 1);
+ }
+ SANDBOX_ASSERT(next == MAX_SYSCALL + 1);
+}
+#endif
+
+SANDBOX_TEST(SyscallIterator, Invalid) {
+ for (int i = 0; i < 2; ++i) {
+ bool invalid_only = !i; // Testing both |invalid_only| cases.
+ SyscallIterator iter(invalid_only);
+ uint32_t next = iter.Next();
+
+ while (next < MAX_SYSCALL + 1) {
+ next = iter.Next();
+ }
+
+ SANDBOX_ASSERT(next == MAX_SYSCALL + 1);
+ while (next < 0x7FFFFFFFu) {
+ next = iter.Next();
+ }
+
+ // The iterator should return the signed/unsigned corner cases.
+ SANDBOX_ASSERT(next == 0x7FFFFFFFu);
+ next = iter.Next();
+ SANDBOX_ASSERT(next == 0x80000000u);
+ SANDBOX_ASSERT(!iter.Done());
+ next = iter.Next();
+ SANDBOX_ASSERT(iter.Done());
+ SANDBOX_ASSERT(next == 0xFFFFFFFFu);
+ }
+}
+
+SANDBOX_TEST(SyscallIterator, InvalidOnly) {
+ bool invalid_only = true;
+ SyscallIterator iter(invalid_only);
+ uint32_t next = iter.Next();
+ // We're assuming MIN_SYSCALL == 0 for all architectures,
+ // this is currently valid for Intel and ARM EABI.
+ // First invalid syscall should then be |MAX_PUBLIC_SYSCALL + 1|.
+ SANDBOX_ASSERT(MIN_SYSCALL == 0);
+ SANDBOX_ASSERT(next == MAX_PUBLIC_SYSCALL + 1);
+
+#if defined(__arm__)
+ next = iter.Next();
+ // The iterator should skip until the last invalid syscall in this range.
+ SANDBOX_ASSERT(next == MIN_PRIVATE_SYSCALL - 1);
+ while (next <= MAX_PRIVATE_SYSCALL) {
+ next = iter.Next();
+ }
+
+ next = iter.Next();
+ // The iterator should skip until the last invalid syscall in this range.
+ SANDBOX_ASSERT(next == MIN_GHOST_SYSCALL - 1);
+ while (next <= MAX_SYSCALL) {
+ next = iter.Next();
+ }
+ SANDBOX_ASSERT(next == MAX_SYSCALL + 1);
+#endif
+}
+
+} // namespace
+
diff --git a/sandbox/linux/seccomp-bpf/verifier.cc b/sandbox/linux/seccomp-bpf/verifier.cc
index 882e96f..343a6b4 100644
--- a/sandbox/linux/seccomp-bpf/verifier.cc
+++ b/sandbox/linux/seccomp-bpf/verifier.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
#include "sandbox/linux/seccomp-bpf/verifier.h"
@@ -17,7 +18,8 @@ bool Verifier::verifyBPF(const std::vector<struct sock_filter>& program,
return false;
}
Sandbox::EvaluateSyscall evaluateSyscall = evaluators.begin()->first;
- for (int nr = MIN_SYSCALL-1; nr <= static_cast<int>(MAX_SYSCALL)+1; ++nr) {
+ for (SyscallIterator iter(false); !iter.Done(); ) {
+ uint32_t sysnum = iter.Next();
// We ideally want to iterate over the full system call range and values
// just above and just below this range. This gives us the full result set
// of the "evaluators".
@@ -25,17 +27,18 @@ bool Verifier::verifyBPF(const std::vector<struct sock_filter>& program,
// indicates either i386 or x86-64; and a set bit 30 indicates x32. And
// unless we pay attention to setting this bit correctly, an early check in
// our BPF program will make us fail with a misleading error code.
+ struct arch_seccomp_data data = { sysnum, SECCOMP_ARCH };
#if defined(__i386__) || defined(__x86_64__)
#if defined(__x86_64__) && defined(__ILP32__)
- int sysnum = nr | 0x40000000;
+ if (!(sysnum & 0x40000000u)) {
+ continue;
+ }
#else
- int sysnum = nr & ~0x40000000;
+ if (sysnum & 0x40000000u) {
+ continue;
+ }
#endif
-#else
- int sysnum = nr;
#endif
-
- struct arch_seccomp_data data = { sysnum, SECCOMP_ARCH };
ErrorCode code = evaluateSyscall(sysnum);
uint32_t computedRet = evaluateBPF(program, data, err);
if (*err) {