summaryrefslogtreecommitdiffstats
path: root/sandbox
diff options
context:
space:
mode:
authormarkus@chromium.org <markus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-31 06:33:33 +0000
committermarkus@chromium.org <markus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-31 06:33:33 +0000
commit4321b18bf08725bd21026ffc4f65bccd842810b3 (patch)
tree878eaf2b15112b393a7d7bbf45b29c2c2dadcc38 /sandbox
parent5d13e7a4e1d4424daa4ecd2ef4431bece617cd30 (diff)
downloadchromium_src-4321b18bf08725bd21026ffc4f65bccd842810b3.zip
chromium_src-4321b18bf08725bd21026ffc4f65bccd842810b3.tar.gz
chromium_src-4321b18bf08725bd21026ffc4f65bccd842810b3.tar.bz2
Refactored ErrorCode into it's own class. Removed operators and made them
explicit functions. Added code that allows testing of the sandbox infrastructure with Valgrind (this currently only works when building with the Makefile; we still need to decide if and how we want to expose this to our unittests). Added a way to unittest the ErrorCode class. BUG=n/a TEST=sandbox_linux_unittests Review URL: https://chromiumcodereview.appspot.com/10833044 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@154397 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r--sandbox/linux/sandbox_linux.gypi3
-rw-r--r--sandbox/linux/seccomp-bpf/Makefile16
-rw-r--r--sandbox/linux/seccomp-bpf/demo.cc10
-rw-r--r--sandbox/linux/seccomp-bpf/errorcode.cc103
-rw-r--r--sandbox/linux/seccomp-bpf/errorcode.h132
-rw-r--r--sandbox/linux/seccomp-bpf/errorcode_unittest.cc77
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf.cc109
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf.h113
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc30
-rw-r--r--sandbox/linux/seccomp-bpf/util.cc2
-rw-r--r--sandbox/linux/seccomp-bpf/util.h6
-rw-r--r--sandbox/linux/seccomp-bpf/verifier.cc4
-rw-r--r--sandbox/linux/seccomp-bpf/verifier.h6
13 files changed, 449 insertions, 162 deletions
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi
index 6d59816..64b8b91 100644
--- a/sandbox/linux/sandbox_linux.gypi
+++ b/sandbox/linux/sandbox_linux.gypi
@@ -55,6 +55,7 @@
'sources': [
'seccomp-bpf/bpf_tests.cc',
'seccomp-bpf/bpf_tests.h',
+ 'seccomp-bpf/errorcode_unittest.cc',
'seccomp-bpf/sandbox_bpf_unittest.cc',
],
}],
@@ -66,6 +67,8 @@
'sources': [
'seccomp-bpf/die.cc',
'seccomp-bpf/die.h',
+ 'seccomp-bpf/errorcode.cc',
+ 'seccomp-bpf/errorcode.h',
'seccomp-bpf/sandbox_bpf.cc',
'seccomp-bpf/sandbox_bpf.h',
'seccomp-bpf/verifier.cc',
diff --git a/sandbox/linux/seccomp-bpf/Makefile b/sandbox/linux/seccomp-bpf/Makefile
index 5753124..a8a2ca3 100644
--- a/sandbox/linux/seccomp-bpf/Makefile
+++ b/sandbox/linux/seccomp-bpf/Makefile
@@ -1,8 +1,8 @@
-CFLAGS = -g -O3 -Wall -Werror -Wextra -Wno-missing-field-initializers -fPIC -I.
-CPPFLAGS = -D_GNU_SOURCE -DSECCOMP_BPF_STANDALONE -iquote ../../..
-LDFLAGS = -g -lpthread
+DEF_CFLAGS = -g -O3 -Wall -Werror -Wextra -Wno-missing-field-initializers -fPIC -I.
+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 util verifier
+MODS := demo sandbox_bpf die errorcode 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)
@@ -20,11 +20,11 @@ clean:
-include $(DEP_FILES)
demo32: ${OBJS32}
- ${CXX} -m32 -o $@ $+ ${LDFLAGS}
+ ${CXX} -m32 -o $@ $+ ${DEF_LDFLAGS} ${LDFLAGS}
demo64: ${OBJS64}
- ${CXX} -m64 -o $@ $+ ${LDFLAGS}
+ ${CXX} -m64 -o $@ $+ ${DEF_LDFLAGS} ${LDFLAGS}
.cc.o32:
- ${CXX} -m32 ${CFLAGS} ${CPPFLAGS} ${DEPFLAGS} -c -o $@ $<
+ ${CXX} -m32 ${DEF_CFLAGS} ${DEF_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} ${DEPFLAGS} -c -o $@ $<
.cc.o64:
- ${CXX} -m64 ${CFLAGS} ${CPPFLAGS} ${DEPFLAGS} -c -o $@ $<
+ ${CXX} -m64 ${DEF_CFLAGS} ${DEF_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} ${DEPFLAGS} -c -o $@ $<
diff --git a/sandbox/linux/seccomp-bpf/demo.cc b/sandbox/linux/seccomp-bpf/demo.cc
index ebedcbe..02fd8a0 100644
--- a/sandbox/linux/seccomp-bpf/demo.cc
+++ b/sandbox/linux/seccomp-bpf/demo.cc
@@ -28,6 +28,8 @@
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
#include "sandbox/linux/seccomp-bpf/util.h"
+using playground2::arch_seccomp_data;
+using playground2::ErrorCode;
using playground2::Sandbox;
using playground2::Util;
@@ -135,7 +137,7 @@ static intptr_t defaultHandler(const struct arch_seccomp_data& data,
return -ERR;
}
-static Sandbox::ErrorCode evaluator(int sysno) {
+static ErrorCode evaluator(int sysno) {
switch (sysno) {
#if defined(__NR_accept)
case __NR_accept: case __NR_accept4:
@@ -220,7 +222,7 @@ static Sandbox::ErrorCode evaluator(int sysno) {
case __NR_time:
case __NR_uname:
case __NR_write: case __NR_writev:
- return Sandbox::SB_ALLOWED;
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
// The following system calls are temporarily permitted. This must be
// tightened later. But we currently don't implement enough of the sandboxing
@@ -252,11 +254,11 @@ static Sandbox::ErrorCode evaluator(int sysno) {
case __NR_clone:
case __NR_munmap: case __NR_mprotect: case __NR_madvise:
case __NR_remap_file_pages:
- return Sandbox::SB_ALLOWED;
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
// Everything that isn't explicitly allowed is denied.
default:
- return Sandbox::ErrorCode(defaultHandler, NULL);
+ return Sandbox::Trap(defaultHandler, NULL);
}
}
diff --git a/sandbox/linux/seccomp-bpf/errorcode.cc b/sandbox/linux/seccomp-bpf/errorcode.cc
new file mode 100644
index 0000000..cc79cb6
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/errorcode.cc
@@ -0,0 +1,103 @@
+// 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"
+
+
+namespace playground2 {
+
+ErrorCode::ErrorCode(int err) {
+ switch (err) {
+ case ERR_ALLOWED:
+ err_ = SECCOMP_RET_ALLOW;
+ error_type_ = ET_SIMPLE;
+ break;
+ case ERR_MIN_ERRNO ... ERR_MAX_ERRNO:
+ err_ = SECCOMP_RET_ERRNO + err;
+ error_type_ = ET_SIMPLE;
+ break;
+ default:
+ SANDBOX_DIE("Invalid use of ErrorCode object");
+ }
+}
+
+ErrorCode::ErrorCode(ErrorCode::TrapFnc fnc, const void *aux, uint16_t id)
+ : error_type_(ET_TRAP),
+ fnc_(fnc),
+ aux_(const_cast<void *>(aux)),
+ err_(SECCOMP_RET_TRAP + id) {
+}
+
+ErrorCode::ErrorCode(int argno, ArgType width, Operation op, uint64_t value,
+ const ErrorCode *passed, const ErrorCode *failed)
+ : error_type_(ET_COND),
+ value_(value),
+ argno_(argno),
+ width_(width),
+ op_(op),
+ passed_(passed),
+ failed_(failed),
+ err_(SECCOMP_RET_INVALID) {
+ if (op < 0 || op >= OP_NUM_OPS) {
+ SANDBOX_DIE("Invalid opcode in BPF sandbox rules");
+ }
+}
+
+bool ErrorCode::Equals(const ErrorCode& err) const {
+ if (error_type_ == ET_INVALID || err.error_type_ == ET_INVALID) {
+ SANDBOX_DIE("Dereferencing invalid ErrorCode");
+ }
+ if (error_type_ != err.error_type_) {
+ return false;
+ }
+ if (error_type_ == ET_SIMPLE || error_type_ == ET_TRAP) {
+ return err_ == err.err_;
+ } else if (error_type_ == ET_COND) {
+ return value_ == err.value_ &&
+ argno_ == err.argno_ &&
+ width_ == err.width_ &&
+ op_ == err.op_ &&
+ passed_->Equals(*err.passed_) &&
+ failed_->Equals(*err.failed_);
+ } else {
+ SANDBOX_DIE("Corrupted ErrorCode");
+ }
+}
+
+bool ErrorCode::LessThan(const ErrorCode& err) const {
+ // Implementing a "LessThan()" operator allows us to use ErrorCode objects
+ // as keys in STL containers; most notably, it also allows us to put them
+ // into std::set<>. Actual ordering is not important as long as it is
+ // deterministic.
+ if (error_type_ == ET_INVALID || err.error_type_ == ET_INVALID) {
+ SANDBOX_DIE("Dereferencing invalid ErrorCode");
+ }
+ if (error_type_ != err.error_type_) {
+ return error_type_ < err.error_type_;
+ } else {
+ if (error_type_ == ET_SIMPLE || error_type_ == ET_TRAP) {
+ return err_ < err.err_;
+ } else if (error_type_ == ET_COND) {
+ if (value_ != err.value_) {
+ return value_ < err.value_;
+ } else if (argno_ != err.argno_) {
+ return argno_ < err.argno_;
+ } else if (width_ != err.width_) {
+ return width_ < err.width_;
+ } else if (op_ != err.op_) {
+ return op_ < err.op_;
+ } else if (!passed_->Equals(*err.passed_)) {
+ return passed_->LessThan(*err.passed_);
+ } else if (!failed_->Equals(*err.failed_)) {
+ return failed_->LessThan(*err.failed_);
+ } else {
+ return false;
+ }
+ } else {
+ SANDBOX_DIE("Corrupted ErrorCode");
+ }
+ }
+}
+
+} // namespace
diff --git a/sandbox/linux/seccomp-bpf/errorcode.h b/sandbox/linux/seccomp-bpf/errorcode.h
new file mode 100644
index 0000000..53622c4
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/errorcode.h
@@ -0,0 +1,132 @@
+// 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_ERRORCODE_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__
+
+namespace playground2 {
+
+struct arch_seccomp_data;
+
+// This class holds all the possible values that can returned by a sandbox
+// policy.
+// We can either wrap a symbolic ErrorCode (i.e. ERR_XXX enum values), an
+// errno value (in the range 1..4095), a pointer to a TrapFnc callback
+// handling a SECCOMP_RET_TRAP trap, or a complex constraint.
+// All of the commonly used values are stored in the "err_" field. So, code
+// that is using the ErrorCode class typically operates on a single 32bit
+// field.
+class ErrorCode {
+ public:
+ enum {
+ // Allow this system call.
+ ERR_ALLOWED = 0x0000,
+
+ // Deny the system call with a particular "errno" value.
+ ERR_MIN_ERRNO = 1,
+ ERR_MAX_ERRNO = 4095,
+
+ // This code should never be used directly, it is used internally only.
+ ERR_INVALID = -1,
+ };
+
+ // TrapFnc is a pointer to a function that handles Seccomp traps in
+ // user-space. The seccomp policy can request that a trap handler gets
+ // installed; it does so by returning a suitable ErrorCode() from the
+ // syscallEvaluator. See the ErrorCode() constructor for how to pass in
+ // the function pointer.
+ // Please note that TrapFnc is executed from signal context and must be
+ // async-signal safe:
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
+ typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void *aux);
+
+ enum ArgType {
+ TP_32BIT, TP_64BIT,
+ };
+
+ enum Operation {
+ OP_EQUAL, OP_GREATER, OP_GREATER_EQUAL, OP_HAS_BITS,
+ OP_NUM_OPS,
+ };
+
+ // We allow the default constructor, as it makes the ErrorCode class
+ // much easier to use. But if we ever encounter an invalid ErrorCode
+ // when compiling a BPF filter, we deliberately generate an invalid
+ // program that will get flagged both by our Verifier class and by
+ // the Linux kernel.
+ ErrorCode() :
+ error_type_(ET_INVALID),
+ err_(SECCOMP_RET_INVALID) {
+ }
+ explicit ErrorCode(int err);
+
+ // For all practical purposes, ErrorCodes are treated as if they were
+ // structs. The copy constructor and assignment operator are trivial and
+ // we do not need to explicitly specify them.
+ // Most notably, it is in fact perfectly OK to directly copy the passed_ and
+ // failed_ field. They only ever get set by our private constructor, and the
+ // callers handle life-cycle management for these objects.
+
+ // Destructor
+ ~ErrorCode() { }
+
+ bool Equals(const ErrorCode& err) const;
+ bool LessThan(const ErrorCode& err) const;
+
+ uint32_t err() const { return err_; }
+
+ struct LessThan {
+ bool operator()(const ErrorCode& a, const ErrorCode& b) const {
+ return a.LessThan(b);
+ }
+ };
+
+ private:
+ friend class Sandbox;
+ friend class Verifier;
+
+ enum ErrorType {
+ ET_INVALID, ET_SIMPLE, ET_TRAP, ET_COND,
+ };
+
+ // If we are wrapping a callback, we must assign a unique id. This id is
+ // how the kernel tells us which one of our different SECCOMP_RET_TRAP
+ // cases has been triggered.
+ ErrorCode(TrapFnc fnc, const void *aux, uint16_t id);
+
+ // Some system calls require inspection of arguments. This constructor
+ // allows us to specify additional constraints.
+ ErrorCode(int argno, ArgType width, Operation op, uint64_t value,
+ const ErrorCode *passed, const ErrorCode *failed);
+
+ ErrorType error_type_;
+
+ union {
+ // Fields needed for SECCOMP_RET_TRAP callbacks
+ struct {
+ TrapFnc fnc_; // Callback function and arg, if trap was
+ void *aux_; // triggered by the kernel's BPF filter.
+ };
+
+ // Fields needed when inspecting additional arguments.
+ struct {
+ uint64_t value_; // Value that we are comparing with.
+ int argno_; // Syscall arg number that we are inspecting.
+ ArgType width_; // Whether we are looking at a 32/64bit value.
+ Operation op_; // Comparison operation.
+ const ErrorCode *passed_; // Value to be returned if comparison passed,
+ const ErrorCode *failed_; // or if it failed.
+ };
+ };
+
+ // 32bit field used for all possible types of ErrorCode values. This is
+ // the value that uniquely identifies any ErrorCode and it (typically) can
+ // be emitted directly into a BPF filter program.
+ uint32_t err_;
+
+};
+
+} // namespace
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__
diff --git a/sandbox/linux/seccomp-bpf/errorcode_unittest.cc b/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
new file mode 100644
index 0000000..21f889e
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
@@ -0,0 +1,77 @@
+// 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/tests/unit_tests.h"
+
+using namespace playground2;
+
+namespace {
+
+SANDBOX_TEST(ErrorCode, ErrnoConstructor) {
+ ErrorCode e0;
+ SANDBOX_ASSERT(e0.err() == SECCOMP_RET_INVALID);
+
+ ErrorCode e1(ErrorCode::ERR_ALLOWED);
+ SANDBOX_ASSERT(e1.err() == SECCOMP_RET_ALLOW);
+
+ ErrorCode e2(EPERM);
+ SANDBOX_ASSERT(e2.err() == SECCOMP_RET_ERRNO + EPERM);
+
+ ErrorCode e3 = Sandbox::Trap(NULL, NULL);
+ SANDBOX_ASSERT((e3.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP);
+}
+
+SANDBOX_TEST(ErrorCode, Trap) {
+ ErrorCode e0 = Sandbox::Trap(NULL, "a");
+ ErrorCode e1 = Sandbox::Trap(NULL, "b");
+ SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) + 1 ==
+ (e1.err() & SECCOMP_RET_DATA));
+
+ ErrorCode e2 = Sandbox::Trap(NULL, "a");
+ SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) ==
+ (e2.err() & SECCOMP_RET_DATA));
+}
+
+SANDBOX_TEST(ErrorCode, Equals) {
+ ErrorCode e1(ErrorCode::ERR_ALLOWED);
+ ErrorCode e2(ErrorCode::ERR_ALLOWED);
+ SANDBOX_ASSERT(e1.Equals(e1));
+ SANDBOX_ASSERT(e1.Equals(e2));
+ SANDBOX_ASSERT(e2.Equals(e1));
+
+ ErrorCode e3(EPERM);
+ SANDBOX_ASSERT(!e1.Equals(e3));
+
+ ErrorCode e4 = Sandbox::Trap(NULL, "a");
+ ErrorCode e5 = Sandbox::Trap(NULL, "b");
+ ErrorCode e6 = Sandbox::Trap(NULL, "a");
+ SANDBOX_ASSERT(!e1.Equals(e4));
+ SANDBOX_ASSERT(!e3.Equals(e4));
+ SANDBOX_ASSERT(!e5.Equals(e4));
+ SANDBOX_ASSERT( e6.Equals(e4));
+}
+
+SANDBOX_TEST(ErrorCode, LessThan) {
+ ErrorCode e1(ErrorCode::ERR_ALLOWED);
+ ErrorCode e2(ErrorCode::ERR_ALLOWED);
+ SANDBOX_ASSERT(!e1.LessThan(e1));
+ SANDBOX_ASSERT(!e1.LessThan(e2));
+ SANDBOX_ASSERT(!e2.LessThan(e1));
+
+ ErrorCode e3(EPERM);
+ SANDBOX_ASSERT(!e1.LessThan(e3));
+ SANDBOX_ASSERT( e3.LessThan(e1));
+
+ ErrorCode e4 = Sandbox::Trap(NULL, "a");
+ ErrorCode e5 = Sandbox::Trap(NULL, "b");
+ ErrorCode e6 = Sandbox::Trap(NULL, "a");
+ SANDBOX_ASSERT(e1.LessThan(e4));
+ SANDBOX_ASSERT(e3.LessThan(e4));
+ SANDBOX_ASSERT(e4.LessThan(e5));
+ SANDBOX_ASSERT(!e4.LessThan(e6));
+ SANDBOX_ASSERT(!e6.LessThan(e4));
+}
+
+} // namespace
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
index 73efcfa..f7c8511 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -14,17 +14,17 @@ namespace playground2 {
// We define a really simple sandbox policy. It is just good enough for us
// to tell that the sandbox has actually been activated.
-Sandbox::ErrorCode Sandbox::probeEvaluator(int signo) {
+ErrorCode Sandbox::probeEvaluator(int signo) {
switch (signo) {
case __NR_getpid:
// Return EPERM so that we can check that the filter actually ran.
- return EPERM;
+ return ErrorCode(EPERM);
case __NR_exit_group:
// Allow exit() with a non-default return code.
- return SB_ALLOWED;
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
default:
// Make everything else fail in an easily recognizable way.
- return EINVAL;
+ return ErrorCode(EINVAL);
}
}
@@ -34,12 +34,12 @@ void Sandbox::probeProcess(void) {
}
}
-Sandbox::ErrorCode Sandbox::allowAllEvaluator(int signo) {
+ErrorCode Sandbox::allowAllEvaluator(int signo) {
if (signo < static_cast<int>(MIN_SYSCALL) ||
signo > static_cast<int>(MAX_SYSCALL)) {
- return ENOSYS;
+ return ErrorCode(ENOSYS);
}
- return Sandbox::SB_ALLOWED;
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
}
void Sandbox::tryVsyscallProcess(void) {
@@ -143,6 +143,15 @@ bool Sandbox::RunFunctionInPolicy(void (*CodeInSandbox)(),
}
bool Sandbox::kernelSupportSeccompBPF(int proc_fd) {
+#if defined(SECCOMP_BPF_VALGRIND_HACKS)
+ if (RUNNING_ON_VALGRIND) {
+ // Valgrind doesn't like our run-time test. Disable testing and assume we
+ // always support sandboxing. This feature should only ever be enabled when
+ // debugging.
+ return true;
+ }
+#endif
+
return RunFunctionInPolicy(probeProcess, Sandbox::probeEvaluator, proc_fd) &&
RunFunctionInPolicy(tryVsyscallProcess, Sandbox::allowAllEvaluator,
proc_fd);
@@ -255,10 +264,10 @@ bool Sandbox::isSingleThreaded(int proc_fd) {
return true;
}
-static bool isDenied(const Sandbox::ErrorCode& code) {
- return (code & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP ||
- (code >= (SECCOMP_RET_ERRNO + 1) &&
- code <= (SECCOMP_RET_ERRNO + 4095));
+bool Sandbox::isDenied(const ErrorCode& code) {
+ return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP ||
+ (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) &&
+ code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO));
}
void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator,
@@ -277,7 +286,8 @@ void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator,
sysnum <= (MAX_SYSCALL & ~0x40000000u);
++sysnum) {
if (!isDenied(syscallEvaluator(sysnum))) {
- SANDBOX_DIE("In x32 mode, you should not allow any non-x32 system calls");
+ SANDBOX_DIE("In x32 mode, you should not allow any non-x32 "
+ "system calls");
}
}
#else
@@ -359,8 +369,7 @@ void Sandbox::installFilter(bool quiet) {
program->push_back((struct sock_filter)
BPF_STMT(BPF_RET+BPF_K,
- ErrorCode(bpfFailure,
- "Invalid audit architecture in BPF filter")));
+ Kill("Invalid audit architecture in BPF filter").err()));
// Grab the system call number, so that we can implement jump tables.
program->push_back((struct sock_filter)
@@ -379,8 +388,7 @@ void Sandbox::installFilter(bool quiet) {
#endif
program->push_back((struct sock_filter)
BPF_STMT(BPF_RET+BPF_K,
- ErrorCode(bpfFailure,
- "Illegal mixing of system call ABIs")));
+ Kill("Illegal mixing of system call ABIs").err()));
#endif
@@ -425,15 +433,22 @@ void Sandbox::installFilter(bool quiet) {
// Release memory that is no longer needed
evaluators_.clear();
+ errMap_.clear();
- // Install BPF filter program
- if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
- SANDBOX_DIE(quiet
- ? NULL : "Kernel refuses to enable no-new-privs");
- } else {
- if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
- SANDBOX_DIE(quiet
- ? NULL : "Kernel refuses to turn on BPF filters");
+#if defined(SECCOMP_BPF_VALGRIND_HACKS)
+ // Valgrind is really not happy about our sandbox. Disable it when running
+ // in Valgrind. This feature is dangerous and should never be enabled by
+ // default. We protect it behind a pre-processor option.
+ if (!RUNNING_ON_VALGRIND)
+#endif
+ {
+ // Install BPF filter program
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ SANDBOX_DIE(quiet ? NULL : "Kernel refuses to enable no-new-privs");
+ } else {
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
+ SANDBOX_DIE(quiet ? NULL : "Kernel refuses to turn on BPF filters");
+ }
}
}
@@ -453,7 +468,7 @@ void Sandbox::findRanges(Ranges *ranges) {
sysnum <= MAX_SYSCALL + 1;
++sysnum) {
ErrorCode err = evaluateSyscall(static_cast<int>(sysnum));
- if (err != oldErr) {
+ if (!err.Equals(oldErr)) {
ranges->push_back(Range(oldSysnum, sysnum-1, oldErr));
oldSysnum = sysnum;
oldErr = err;
@@ -468,9 +483,9 @@ void Sandbox::findRanges(Ranges *ranges) {
// 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 != evaluateSyscall(std::numeric_limits<int>::max()) ||
- oldErr != evaluateSyscall(std::numeric_limits<int>::min()) ||
- oldErr != evaluateSyscall(-1)) {
+ 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(
@@ -514,7 +529,7 @@ void Sandbox::emitJumpStatements(Program *program, RetInsns *rets,
// Since all branches in BPF programs have to be forward branches, we
// keep track of our current instruction pointer and then fix up the
// branch when we emit the BPF_RET statement in emitReturnStatements().
- (*rets)[start->err].push_back(FixUp(jmp, false));
+ (*rets)[start->err.err()].push_back(FixUp(jmp, false));
} else {
// Sub-divide the list of ranges and continue recursively.
emitJumpStatements(program, rets, start, mid);
@@ -526,7 +541,7 @@ void Sandbox::emitJumpStatements(Program *program, RetInsns *rets,
// We narrowed things down to a single range object. Remember instruction
// pointer and exit code, so that we can patch up the target of the jump
// instruction in emitReturnStatements().
- (*rets)[mid->err].push_back(FixUp(jmp, true));
+ (*rets)[mid->err.err()].push_back(FixUp(jmp, true));
} else {
// We now know where the block of instructions for the "true" comparison
// starts. Patch up the jump target of the BPF_JMP instruction that we
@@ -584,8 +599,8 @@ void Sandbox::sigSys(int nr, siginfo_t *info, void *void_context) {
if (nr != SIGSYS || info->si_code != SYS_SECCOMP || !void_context ||
info->si_errno <= 0 ||
static_cast<size_t>(info->si_errno) > trapArraySize_) {
- // SANDBOX_DIE() can call LOG(FATAL). This is not normally async-signal safe
- // and can lead to bugs. We should eventually implement a different
+ // SANDBOX_DIE() can call LOG(FATAL). This is not normally async-signal
+ // safe and can lead to bugs. We should eventually implement a different
// logging and reporting mechanism that is safe to be called from
// the sigSys() handler.
// TODO: If we feel confident that our code otherwise works correctly, we
@@ -647,19 +662,16 @@ void Sandbox::sigSys(int nr, siginfo_t *info, void *void_context) {
return;
}
-intptr_t Sandbox::bpfFailure(const struct arch_seccomp_data&, void *aux) {
- SANDBOX_DIE(static_cast<char *>(aux));
-}
-
-int Sandbox::getTrapId(Sandbox::TrapFnc fnc, const void *aux) {
+ErrorCode Sandbox::Trap(ErrorCode::TrapFnc fnc, const void *aux) {
// Each unique pair of TrapFnc and auxiliary data make up a distinct instance
// of a SECCOMP_RET_TRAP.
- std::pair<TrapFnc, const void *> key(fnc, aux);
+ std::pair<ErrorCode::TrapFnc, const void *> key(fnc, aux);
TrapIds::const_iterator iter = trapIds_.find(key);
+ uint16_t id;
if (iter != trapIds_.end()) {
// We have seen this pair before. Return the same id that we assigned
// earlier.
- return iter->second;
+ id = iter->second;
} else {
// This is a new pair. Remember it and assign a new id.
// Please note that we have to store traps in memory that doesn't get
@@ -669,12 +681,12 @@ int Sandbox::getTrapId(Sandbox::TrapFnc fnc, const void *aux) {
if (!traps_) {
traps_ = new Traps();
}
- Traps::size_type id = traps_->size() + 1;
- if (id > SECCOMP_RET_DATA) {
+ if (traps_->size() >= SECCOMP_RET_DATA) {
// In practice, this is pretty much impossible to trigger, as there
// are other kernel limitations that restrict overall BPF program sizes.
SANDBOX_DIE("Too many SECCOMP_RET_TRAP callback instances");
}
+ id = traps_->size() + 1;
traps_->push_back(ErrorCode(fnc, aux, id));
trapIds_[key] = id;
@@ -687,16 +699,27 @@ int Sandbox::getTrapId(Sandbox::TrapFnc fnc, const void *aux) {
// signal handler, where we can safely do so.
trapArray_ = &(*traps_)[0];
trapArraySize_ = id;
- return id;
}
+
+ ErrorCode err = ErrorCode(fnc, aux, id);
+ return errMap_[err.err()] = err;
+}
+
+intptr_t Sandbox::bpfFailure(const struct arch_seccomp_data&, void *aux) {
+ SANDBOX_DIE(static_cast<char *>(aux));
+}
+
+ErrorCode Sandbox::Kill(const char *msg) {
+ return Trap(bpfFailure, const_cast<char *>(msg));
}
Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN;
int Sandbox::proc_fd_ = -1;
Sandbox::Evaluators Sandbox::evaluators_;
+Sandbox::ErrMap Sandbox::errMap_;
Sandbox::Traps *Sandbox::traps_ = NULL;
Sandbox::TrapIds Sandbox::trapIds_;
-Sandbox::ErrorCode *Sandbox::trapArray_ = NULL;
+ErrorCode *Sandbox::trapArray_ = NULL;
size_t Sandbox::trapArraySize_ = 0;
} // namespace
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
index c173d78..5a177ad 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.h
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef SANDBOX_BPF_H__
-#define SANDBOX_BPF_H__
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
#include <endian.h>
#include <errno.h>
@@ -45,6 +45,13 @@
#include "base/logging.h"
#endif
+#if defined(SECCOMP_BPF_VALGRIND_HACKS)
+#ifndef SECCOMP_BPF_STANDALONE
+#include "base/third_party/valgrind/valgrind.h"
+#endif
+#endif
+
+
// The Seccomp2 kernel ABI is not part of older versions of glibc.
// As we can't break compilation with these versions of the library,
// we explicitly define all missing symbols.
@@ -145,7 +152,9 @@
#endif
#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+namespace playground2 {
struct arch_seccomp_data {
int nr;
@@ -160,7 +169,7 @@ struct arch_sigsys {
unsigned int arch;
};
-#ifdef SECCOMP_BPF_STANDALONE
+#if defined(SECCOMP_BPF_STANDALONE)
#define arraysize(x) sizeof(x)/sizeof(*(x)))
#define HANDLE_EINTR TEMP_FAILURE_RETRY
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
@@ -169,9 +178,6 @@ struct arch_sigsys {
void operator=(const TypeName&)
#endif
-
-namespace playground2 {
-
class Sandbox {
public:
enum SandboxStatus {
@@ -182,17 +188,6 @@ class Sandbox {
STATUS_ENABLED // The sandbox is now active
};
- enum {
- SB_INVALID = -1,
- SB_ALLOWED = 0x0000,
- SB_INSPECT_ARG_1 = 0x8001,
- SB_INSPECT_ARG_2 = 0x8002,
- SB_INSPECT_ARG_3 = 0x8004,
- SB_INSPECT_ARG_4 = 0x8008,
- SB_INSPECT_ARG_5 = 0x8010,
- SB_INSPECT_ARG_6 = 0x8020
- };
-
// TrapFnc is a pointer to a function that handles Seccomp traps in
// user-space. The seccomp policy can request that a trap handler gets
// installed; it does so by returning a suitable ErrorCode() from the
@@ -203,67 +198,6 @@ class Sandbox {
// http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void *aux);
- class ErrorCode {
- friend class Sandbox;
- public:
- // We can either wrap a symbolic ErrorCode (i.e. enum values), an errno
- // value (in the range 1..4095), or a pointer to a TrapFnc callback
- // handling a SECCOMP_RET_TRAP trap.
- // All of these different values are stored in the "err_" field. So, code
- // that is using the ErrorCode class typically operates on a single 32bit
- // field.
- // This is not only quiet efficient, it also makes the API really easy to
- // use.
- ErrorCode(int err = SB_INVALID)
- : id_(0),
- fnc_(NULL),
- aux_(NULL) {
- switch (err) {
- case SB_INVALID:
- err_ = SECCOMP_RET_INVALID;
- break;
- case SB_ALLOWED:
- err_ = SECCOMP_RET_ALLOW;
- break;
- case SB_INSPECT_ARG_1...SB_INSPECT_ARG_6:
- SANDBOX_DIE("Not implemented");
- break;
- case 1 ... 4095:
- err_ = SECCOMP_RET_ERRNO + err;
- break;
- default:
- SANDBOX_DIE("Invalid use of ErrorCode object");
- }
- }
-
- // If we are wrapping a callback, we must assign a unique id. This id is
- // how the kernel tells us which one of our different SECCOMP_RET_TRAP
- // cases has been triggered.
- // The getTrapId() function assigns one unique id (starting at 1) for
- // each distinct pair of TrapFnc and auxiliary data.
- ErrorCode(TrapFnc fnc, const void *aux, int id = 0) :
- id_(id ? id : getTrapId(fnc, aux)),
- fnc_(fnc),
- aux_(const_cast<void *>(aux)),
- err_(SECCOMP_RET_TRAP + id_) {
- }
-
- // Destructor doesn't need to do anything.
- ~ErrorCode() { }
-
- // Always return the value that goes into the BPF filter program.
- operator uint32_t() const { return err_; }
-
- protected:
- // Fields needed for SECCOMP_RET_TRAP callbacks
- int id_;
- TrapFnc fnc_;
- void *aux_;
-
- // 32bit field used for all possible types of ErrorCode values
- uint32_t err_;
- };
-
enum Operation {
OP_NOP, OP_EQUAL, OP_NOTEQUAL, OP_LESS,
OP_LESS_EQUAL, OP_GREATER, OP_GREATER_EQUAL,
@@ -311,18 +245,25 @@ class Sandbox {
static void setSandboxPolicy(EvaluateSyscall syscallEvaluator,
EvaluateArguments argumentEvaluator);
+ // We can use ErrorCode to request calling of a trap handler. This method
+ // performs the required wrapping of the callback function into an
+ // ErrorCode object.
+ static ErrorCode Trap(ErrorCode::TrapFnc fnc, const void *aux);
+
+ // Kill the program and print an error message.
+ static ErrorCode Kill(const char *msg);
+
// This is the main public entry point. It finds all system calls that
// need rewriting, sets up the resources needed by the sandbox, and
// enters Seccomp mode.
static void startSandbox() { startSandboxInternal(false); }
- protected:
- // Get a file descriptor pointing to "/proc", if currently available.
- static int getProcFd() { return proc_fd_; }
-
private:
+ friend class ErrorCode;
friend class Util;
friend class Verifier;
+
+
struct Range {
Range(uint32_t f, uint32_t t, const ErrorCode& e) :
from(f),
@@ -341,9 +282,13 @@ class Sandbox {
typedef std::vector<Range> Ranges;
typedef std::map<uint32_t, std::vector<FixUp> > RetInsns;
typedef std::vector<struct sock_filter> Program;
+ typedef std::map<uint32_t, ErrorCode> ErrMap;
typedef std::vector<ErrorCode> Traps;
typedef std::map<std::pair<TrapFnc, const void *>, int> TrapIds;
+ // Get a file descriptor pointing to "/proc", if currently available.
+ static int proc_fd() { return proc_fd_; }
+
static ErrorCode probeEvaluator(int signo) __attribute__((const));
static void probeProcess(void);
static ErrorCode allowAllEvaluator(int signo);
@@ -354,6 +299,7 @@ class Sandbox {
int proc_fd);
static void startSandboxInternal(bool quiet);
static bool isSingleThreaded(int proc_fd);
+ static bool isDenied(const ErrorCode& code);
static bool disableFilesystem();
static void policySanityChecks(EvaluateSyscall syscallEvaluator,
EvaluateArguments argumentEvaluator);
@@ -370,6 +316,7 @@ class Sandbox {
static SandboxStatus status_;
static int proc_fd_;
static Evaluators evaluators_;
+ static ErrMap errMap_;
static Traps *traps_;
static TrapIds trapIds_;
static ErrorCode *trapArray_;
@@ -379,4 +326,4 @@ class Sandbox {
} // namespace
-#endif // SANDBOX_BPF_H__
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
index ac25b2b..91b7746 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
@@ -40,17 +40,17 @@ SANDBOX_TEST(SandboxBpf, CallSupportsTwice) {
// A simple blacklist test
-Sandbox::ErrorCode BlacklistNanosleepPolicy(int sysno) {
+ErrorCode BlacklistNanosleepPolicy(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
- return ENOSYS;
+ return ErrorCode(ENOSYS);
}
switch (sysno) {
case __NR_nanosleep:
- return EACCES;
+ return ErrorCode(EACCES);
default:
- return Sandbox::SB_ALLOWED;
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
@@ -64,13 +64,13 @@ BPF_TEST(SandboxBpf, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) {
// Now do a simple whitelist test
-Sandbox::ErrorCode WhitelistGetpidPolicy(int sysno) {
+ErrorCode WhitelistGetpidPolicy(int sysno) {
switch (sysno) {
case __NR_getpid:
case __NR_exit_group:
- return Sandbox::SB_ALLOWED;
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
default:
- return ENOMEM;
+ return ErrorCode(ENOMEM);
}
}
@@ -99,18 +99,18 @@ intptr_t EnomemHandler(const struct arch_seccomp_data& args, void *aux) {
return -ENOMEM;
}
-Sandbox::ErrorCode BlacklistNanosleepPolicySigsys(int sysno) {
+ErrorCode BlacklistNanosleepPolicySigsys(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
- return ENOSYS;
+ return ErrorCode(ENOSYS);
}
switch (sysno) {
case __NR_nanosleep:
- return Sandbox::ErrorCode(EnomemHandler,
+ return Sandbox::Trap(EnomemHandler,
static_cast<void *>(&BlacklistNanosleepPolicySigsysAuxData));
default:
- return Sandbox::SB_ALLOWED;
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
@@ -147,11 +147,11 @@ int SysnoToRandomErrno(int sysno) {
return ((sysno & ~3) >> 2) % 29 + 1;
}
-Sandbox::ErrorCode SyntheticPolicy(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.
- return ENOSYS;
+ return ErrorCode(ENOSYS);
}
// TODO(jorgelo): remove this restriction once crbug.com/141694 is fixed.
@@ -166,9 +166,9 @@ Sandbox::ErrorCode SyntheticPolicy(int sysno) {
if (sysno == __NR_exit_group /* || sysno == __NR_write */) {
// exit_group() is special, we really need it to work.
// write() is needed for BPF_ASSERT() to report a useful error message.
- return Sandbox::SB_ALLOWED;
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
} else {
- return SysnoToRandomErrno(sysno);
+ return ErrorCode(SysnoToRandomErrno(sysno));
}
}
diff --git a/sandbox/linux/seccomp-bpf/util.cc b/sandbox/linux/seccomp-bpf/util.cc
index ac75146..904a169 100644
--- a/sandbox/linux/seccomp-bpf/util.cc
+++ b/sandbox/linux/seccomp-bpf/util.cc
@@ -118,7 +118,7 @@ bool Util::getFds(int transport, void *buf, size_t *len, ...) {
void Util::closeAllBut(int fd, ...) {
int proc_fd;
int fdir;
- if ((proc_fd = Sandbox::getProcFd()) < 0 ||
+ if ((proc_fd = Sandbox::proc_fd()) < 0 ||
(fdir = openat(proc_fd, "self/fd", O_RDONLY|O_DIRECTORY)) < 0) {
SANDBOX_DIE("Cannot access \"/proc/self/fd\"");
}
diff --git a/sandbox/linux/seccomp-bpf/util.h b/sandbox/linux/seccomp-bpf/util.h
index 25e7e42..3e4d41b 100644
--- a/sandbox/linux/seccomp-bpf/util.h
+++ b/sandbox/linux/seccomp-bpf/util.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UTIL_H__
-#define UTIL_H__
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_UTIL_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_UTIL_H__
namespace playground2 {
@@ -16,4 +16,4 @@ class Util {
} // namespace
-#endif // UTIL_H__
+#endif // SANDBOX_LINUX_SECCOMP_BPF_UTIL_H__
diff --git a/sandbox/linux/seccomp-bpf/verifier.cc b/sandbox/linux/seccomp-bpf/verifier.cc
index 52a385e..882e96f 100644
--- a/sandbox/linux/seccomp-bpf/verifier.cc
+++ b/sandbox/linux/seccomp-bpf/verifier.cc
@@ -36,11 +36,11 @@ bool Verifier::verifyBPF(const std::vector<struct sock_filter>& program,
#endif
struct arch_seccomp_data data = { sysnum, SECCOMP_ARCH };
- Sandbox::ErrorCode code = evaluateSyscall(sysnum);
+ ErrorCode code = evaluateSyscall(sysnum);
uint32_t computedRet = evaluateBPF(program, data, err);
if (*err) {
return false;
- } else if (computedRet != code) {
+ } else if (computedRet != code.err()) {
*err = "Exit code from BPF program doesn't match";
return false;
}
diff --git a/sandbox/linux/seccomp-bpf/verifier.h b/sandbox/linux/seccomp-bpf/verifier.h
index a4e5086..c8128ec 100644
--- a/sandbox/linux/seccomp-bpf/verifier.h
+++ b/sandbox/linux/seccomp-bpf/verifier.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef VERIFIER_H__
-#define VERIFIER_H__
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_VERIFIER_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_VERIFIER_H__
#include <linux/filter.h>
@@ -72,4 +72,4 @@ class Verifier {
} // namespace
-#endif // VERIFIER_H__
+#endif // SANDBOX_LINUX_SECCOMP_BPF_VERIFIER_H__