0

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
This commit is contained in:
jorgelo@chromium.org
2012-10-15 20:33:16 +00:00
parent ff297228aa
commit db30283ec8
10 changed files with 404 additions and 112 deletions

@ -1295,8 +1295,7 @@ ErrorCode FlashProcessPolicy_x86_64(int sysno) {
}
ErrorCode BlacklistDebugAndNumaPolicy(int sysno) {
if (sysno < static_cast<int>(MIN_SYSCALL) ||
sysno > static_cast<int>(MAX_SYSCALL)) {
if (!Sandbox::isValidSyscallNumber(sysno)) {
// TODO(jln) we should not have to do that in a trivial policy.
return ErrorCode(ENOSYS);
}
@ -1311,8 +1310,7 @@ ErrorCode BlacklistDebugAndNumaPolicy(int sysno) {
// This will still deny x32 or IA32 calls in 64 bits mode or
// 64 bits system calls in compatibility mode.
ErrorCode AllowAllPolicy(int sysno) {
if (sysno < static_cast<int>(MIN_SYSCALL) ||
sysno > static_cast<int>(MAX_SYSCALL)) {
if (!Sandbox::isValidSyscallNumber(sysno)) {
// TODO(jln) we should not have to do that in a trivial policy.
return ErrorCode(ENOSYS);
} else {
@ -1420,7 +1418,7 @@ bool SandboxSeccompBpf::ShouldEnableSeccompBpf(
return true;
#endif // __arm__
#endif // process_type
#endif // SECCOMP_BPF_SANDBOX
return false;
}

@ -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',
],

@ -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)

@ -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,

@ -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)(),

@ -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

@ -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

@ -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__

@ -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

@ -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) {