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:
@ -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
|
||||
|
91
sandbox/linux/seccomp-bpf/syscall_iterator.cc
Normal file
91
sandbox/linux/seccomp-bpf/syscall_iterator.cc
Normal file
@ -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
|
||||
|
58
sandbox/linux/seccomp-bpf/syscall_iterator.h
Normal file
58
sandbox/linux/seccomp-bpf/syscall_iterator.h
Normal file
@ -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__
|
||||
|
135
sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
Normal file
135
sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
Normal file
@ -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) {
|
||||
|
Reference in New Issue
Block a user