summaryrefslogtreecommitdiffstats
path: root/sandbox
diff options
context:
space:
mode:
authormdempsky@chromium.org <mdempsky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-17 07:24:58 +0000
committermdempsky@chromium.org <mdempsky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-17 07:24:58 +0000
commitfb75e2a68ef588eae97dbdb811aacd9451c90256 (patch)
treeca1012ae3e12dbcb49cdef7ed429fd9d818ae918 /sandbox
parent6f582dc9b0ea1a24e16de8712e420e320ae2ed18 (diff)
downloadchromium_src-fb75e2a68ef588eae97dbdb811aacd9451c90256.zip
chromium_src-fb75e2a68ef588eae97dbdb811aacd9451c90256.tar.gz
chromium_src-fb75e2a68ef588eae97dbdb811aacd9451c90256.tar.bz2
Add domain-specific language for BPF policies
This CL adds basic support for equality testing of system call arguments, and conjunctive and disjunctive combinations of tests. Reland of https://codereview.chromium.org/299743002/ BUG=375497 Review URL: https://codereview.chromium.org/396323002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@283687 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r--sandbox/linux/BUILD.gn5
-rw-r--r--sandbox/linux/bpf_dsl/DEPS3
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl.cc278
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl.h246
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc268
-rw-r--r--sandbox/linux/bpf_dsl/cons.h46
-rw-r--r--sandbox/linux/bpf_dsl/cons_unittest.cc34
-rw-r--r--sandbox/linux/sandbox_linux.gypi3
-rw-r--r--sandbox/linux/sandbox_linux_test_sources.gypi2
9 files changed, 885 insertions, 0 deletions
diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn
index faf25cd..9f481d6 100644
--- a/sandbox/linux/BUILD.gn
+++ b/sandbox/linux/BUILD.gn
@@ -92,6 +92,8 @@ test("sandbox_linux_unittests") {
}
if (use_seccomp_bpf) {
sources += [
+ "bpf_dsl/bpf_dsl_unittest.cc",
+ "bpf_dsl/cons_unittest.cc",
"seccomp-bpf-helpers/baseline_policy_unittest.cc",
"seccomp-bpf/bpf_tests_unittest.cc",
"seccomp-bpf/codegen_unittest.cc",
@@ -129,6 +131,9 @@ test("sandbox_linux_unittests") {
component("seccomp_bpf") {
sources = [
+ "bpf_dsl/bpf_dsl.cc",
+ "bpf_dsl/bpf_dsl.h",
+ "bpf_dsl/cons.h",
"seccomp-bpf/basicblock.cc",
"seccomp-bpf/basicblock.h",
"seccomp-bpf/codegen.cc",
diff --git a/sandbox/linux/bpf_dsl/DEPS b/sandbox/linux/bpf_dsl/DEPS
new file mode 100644
index 0000000..01595a4
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+sandbox/linux/seccomp-bpf",
+]
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.cc b/sandbox/linux/bpf_dsl/bpf_dsl.cc
new file mode 100644
index 0000000..66cd09c
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl.cc
@@ -0,0 +1,278 @@
+// Copyright 2014 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/bpf_dsl/bpf_dsl.h"
+
+#include <errno.h>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+
+using namespace sandbox::bpf_dsl::internal;
+typedef ::sandbox::Trap::TrapFnc TrapFnc;
+
+namespace sandbox {
+namespace bpf_dsl {
+namespace {
+
+class AllowResultExprImpl : public ResultExprImpl {
+ public:
+ AllowResultExprImpl() {}
+ virtual ErrorCode Compile(SandboxBPF* sb) const OVERRIDE {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+
+ private:
+ virtual ~AllowResultExprImpl() {}
+ DISALLOW_COPY_AND_ASSIGN(AllowResultExprImpl);
+};
+
+class ErrorResultExprImpl : public ResultExprImpl {
+ public:
+ explicit ErrorResultExprImpl(int err) : err_(err) {
+ CHECK(err_ >= ErrorCode::ERR_MIN_ERRNO && err_ <= ErrorCode::ERR_MAX_ERRNO);
+ }
+ virtual ErrorCode Compile(SandboxBPF* sb) const OVERRIDE {
+ return ErrorCode(err_);
+ }
+
+ private:
+ virtual ~ErrorResultExprImpl() {}
+ int err_;
+ DISALLOW_COPY_AND_ASSIGN(ErrorResultExprImpl);
+};
+
+class TrapResultExprImpl : public ResultExprImpl {
+ public:
+ TrapResultExprImpl(TrapFnc func, void* arg) : func_(func), arg_(arg) {
+ DCHECK(func_);
+ }
+ virtual ErrorCode Compile(SandboxBPF* sb) const OVERRIDE {
+ return sb->Trap(func_, arg_);
+ }
+
+ private:
+ virtual ~TrapResultExprImpl() {}
+ TrapFnc func_;
+ void* arg_;
+ DISALLOW_COPY_AND_ASSIGN(TrapResultExprImpl);
+};
+
+class IfThenResultExprImpl : public ResultExprImpl {
+ public:
+ IfThenResultExprImpl(BoolExpr cond,
+ ResultExpr then_result,
+ ResultExpr else_result)
+ : cond_(cond), then_result_(then_result), else_result_(else_result) {}
+ virtual ErrorCode Compile(SandboxBPF* sb) const OVERRIDE {
+ return cond_->Compile(
+ sb, then_result_->Compile(sb), else_result_->Compile(sb));
+ }
+
+ private:
+ virtual ~IfThenResultExprImpl() {}
+ BoolExpr cond_;
+ ResultExpr then_result_;
+ ResultExpr else_result_;
+ DISALLOW_COPY_AND_ASSIGN(IfThenResultExprImpl);
+};
+
+class PrimitiveBoolExprImpl : public BoolExprImpl {
+ public:
+ PrimitiveBoolExprImpl(int argno,
+ ErrorCode::ArgType is_32bit,
+ ErrorCode::Operation op,
+ uint64_t value)
+ : argno_(argno), is_32bit_(is_32bit), op_(op), value_(value) {}
+ virtual ErrorCode Compile(SandboxBPF* sb,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const OVERRIDE {
+ return sb->Cond(argno_, is_32bit_, op_, value_, true_ec, false_ec);
+ }
+
+ private:
+ virtual ~PrimitiveBoolExprImpl() {}
+ int argno_;
+ ErrorCode::ArgType is_32bit_;
+ ErrorCode::Operation op_;
+ uint64_t value_;
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveBoolExprImpl);
+};
+
+class NegateBoolExprImpl : public BoolExprImpl {
+ public:
+ explicit NegateBoolExprImpl(BoolExpr cond) : cond_(cond) {}
+ virtual ErrorCode Compile(SandboxBPF* sb,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const OVERRIDE {
+ return cond_->Compile(sb, false_ec, true_ec);
+ }
+
+ private:
+ virtual ~NegateBoolExprImpl() {}
+ BoolExpr cond_;
+ DISALLOW_COPY_AND_ASSIGN(NegateBoolExprImpl);
+};
+
+class AndBoolExprImpl : public BoolExprImpl {
+ public:
+ AndBoolExprImpl(BoolExpr lhs, BoolExpr rhs) : lhs_(lhs), rhs_(rhs) {}
+ virtual ErrorCode Compile(SandboxBPF* sb,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const OVERRIDE {
+ return lhs_->Compile(sb, rhs_->Compile(sb, true_ec, false_ec), false_ec);
+ }
+
+ private:
+ virtual ~AndBoolExprImpl() {}
+ BoolExpr lhs_, rhs_;
+ DISALLOW_COPY_AND_ASSIGN(AndBoolExprImpl);
+};
+
+class OrBoolExprImpl : public BoolExprImpl {
+ public:
+ OrBoolExprImpl(BoolExpr lhs, BoolExpr rhs) : lhs_(lhs), rhs_(rhs) {}
+ virtual ErrorCode Compile(SandboxBPF* sb,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const OVERRIDE {
+ return lhs_->Compile(sb, true_ec, rhs_->Compile(sb, true_ec, false_ec));
+ }
+
+ private:
+ virtual ~OrBoolExprImpl() {}
+ BoolExpr lhs_, rhs_;
+ DISALLOW_COPY_AND_ASSIGN(OrBoolExprImpl);
+};
+
+} // namespace
+
+namespace internal {
+
+BoolExpr ArgEq(int num, size_t size, uint64_t mask, uint64_t val) {
+ CHECK(num >= 0 && num < 6);
+ CHECK(size >= 1 && size <= 8);
+ CHECK_NE(0U, mask) << "zero mask doesn't make sense";
+ CHECK_EQ(val, val & mask) << "val contains masked out bits";
+
+ // TODO(mdempsky): Should we just always use TP_64BIT?
+ const ErrorCode::ArgType arg_type =
+ (size <= 4) ? ErrorCode::TP_32BIT : ErrorCode::TP_64BIT;
+
+ if (mask == static_cast<uint64_t>(-1)) {
+ // Arg == Val
+ return BoolExpr(new const PrimitiveBoolExprImpl(
+ num, arg_type, ErrorCode::OP_EQUAL, val));
+ } else if (mask == val) {
+ // (Arg & Mask) == Mask
+ return BoolExpr(new const PrimitiveBoolExprImpl(
+ num, arg_type, ErrorCode::OP_HAS_ALL_BITS, mask));
+ } else if (val == 0) {
+ // (Arg & Mask) == 0, which is semantically equivalent to !((arg & mask) !=
+ // 0).
+ return !BoolExpr(new const PrimitiveBoolExprImpl(
+ num, arg_type, ErrorCode::OP_HAS_ANY_BITS, mask));
+ } else {
+ CHECK(false) << "Unimplemented ArgEq case";
+ return BoolExpr();
+ }
+}
+
+} // namespace internal
+
+ResultExpr Allow() {
+ return ResultExpr(new const AllowResultExprImpl());
+}
+
+ResultExpr Error(int err) {
+ return ResultExpr(new const ErrorResultExprImpl(err));
+}
+
+ResultExpr Trap(TrapFnc trap_func, void* aux) {
+ return ResultExpr(new const TrapResultExprImpl(trap_func, aux));
+}
+
+BoolExpr operator!(BoolExpr cond) {
+ return BoolExpr(new const NegateBoolExprImpl(cond));
+}
+
+BoolExpr operator&&(BoolExpr lhs, BoolExpr rhs) {
+ return BoolExpr(new const AndBoolExprImpl(lhs, rhs));
+}
+
+BoolExpr operator||(BoolExpr lhs, BoolExpr rhs) {
+ return BoolExpr(new const OrBoolExprImpl(lhs, rhs));
+}
+
+Elser If(BoolExpr cond, ResultExpr then_result) {
+ return Elser(Cons<Elser::Clause>::List()).ElseIf(cond, then_result);
+}
+
+Elser::Elser(Cons<Clause>::List clause_list) : clause_list_(clause_list) {
+}
+
+Elser::Elser(const Elser& elser) : clause_list_(elser.clause_list_) {
+}
+
+Elser::~Elser() {
+}
+
+Elser Elser::ElseIf(BoolExpr cond, ResultExpr then_result) const {
+ return Elser(
+ Cons<Clause>::Make(std::make_pair(cond, then_result), clause_list_));
+}
+
+ResultExpr Elser::Else(ResultExpr else_result) const {
+ // We finally have the default result expression for this
+ // if/then/else sequence. Also, we've already accumulated all
+ // if/then pairs into a list of reverse order (i.e., lower priority
+ // conditions are listed before higher priority ones). E.g., an
+ // expression like
+ //
+ // If(b1, e1).ElseIf(b2, e2).ElseIf(b3, e3).Else(e4)
+ //
+ // will have built up a list like
+ //
+ // [(b3, e3), (b2, e2), (b1, e1)].
+ //
+ // Now that we have e4, we can walk the list and create a ResultExpr
+ // tree like:
+ //
+ // expr = e4
+ // expr = (b3 ? e3 : expr) = (b3 ? e3 : e4)
+ // expr = (b2 ? e2 : expr) = (b2 ? e2 : (b3 ? e3 : e4))
+ // expr = (b1 ? e1 : expr) = (b1 ? e1 : (b2 ? e2 : (b3 ? e3 : e4)))
+ //
+ // and end up with an appropriately chained tree.
+
+ ResultExpr expr = else_result;
+ for (Cons<Clause>::List it = clause_list_; it; it = it->tail()) {
+ Clause clause = it->head();
+ expr = ResultExpr(
+ new const IfThenResultExprImpl(clause.first, clause.second, expr));
+ }
+ return expr;
+}
+
+ResultExpr SandboxBPFDSLPolicy::InvalidSyscall() const {
+ return Error(ENOSYS);
+}
+
+ErrorCode SandboxBPFDSLPolicy::EvaluateSyscall(SandboxBPF* sb,
+ int sysno) const {
+ return EvaluateSyscall(sysno)->Compile(sb);
+}
+
+ErrorCode SandboxBPFDSLPolicy::InvalidSyscall(SandboxBPF* sb) const {
+ return InvalidSyscall()->Compile(sb);
+}
+
+ResultExpr SandboxBPFDSLPolicy::Trap(::sandbox::Trap::TrapFnc trap_func,
+ void* aux) {
+ return bpf_dsl::Trap(trap_func, aux);
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.h b/sandbox/linux/bpf_dsl/bpf_dsl.h
new file mode 100644
index 0000000..d46102a
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl.h
@@ -0,0 +1,246 @@
+// Copyright 2014 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_BPF_DSL_BPF_DSL_H_
+#define SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "sandbox/linux/bpf_dsl/cons.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+class ErrorCode;
+class SandboxBPF;
+}
+
+// The sandbox::bpf_dsl namespace provides a domain-specific language
+// to make writing BPF policies more expressive. In general, the
+// object types all have value semantics (i.e., they can be copied
+// around, returned from or passed to function calls, etc. without any
+// surprising side effects), though not all support assignment.
+//
+// An idiomatic and demonstrative (albeit silly) example of this API
+// would be:
+//
+// #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+//
+// using namespace sandbox::bpf_dsl;
+//
+// class SillyPolicy : public SandboxBPFDSLPolicy {
+// public:
+// SillyPolicy() {}
+// virtual ~SillyPolicy() {}
+// virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
+// if (sysno == __NR_fcntl) {
+// Arg<int> fd(0), cmd(1);
+// Arg<unsigned long> flags(2);
+// const unsigned long kBadFlags = ~(O_ACCMODE | O_NONBLOCK);
+// return If(fd == 0 && cmd == F_SETFL && (flags & kBadFlags) == 0,
+// Allow())
+// .ElseIf(cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC,
+// Error(EMFILE))
+// .Else(Trap(SetFlagHandler, NULL));
+// } else {
+// return Allow();
+// }
+// }
+//
+// private:
+// DISALLOW_COPY_AND_ASSIGN(SillyPolicy);
+// };
+//
+// More generally, the DSL currently supports the following grammar:
+//
+// result = Allow() | Error(errno) | Trap(trap_func, arg)
+// | If(bool, result)[.ElseIf(bool, result)].Else(result)
+// bool = arg == val | (arg & mask) == mask | (arg & mask) == 0
+// | !bool | bool && bool | bool || bool
+//
+// The semantics of each function and operator are intended to be
+// intuitive, but are described in more detail below.
+//
+// (Credit to Sean Parent's "Inheritance is the Base Class of Evil"
+// talk at Going Native 2013 for promoting value semantics via shared
+// pointers to immutable state.)
+
+namespace sandbox {
+namespace bpf_dsl {
+
+// Forward declarations of classes; see below for proper documentation.
+class Elser;
+namespace internal {
+class ResultExprImpl;
+class BoolExprImpl;
+}
+
+// ResultExpr is an opaque reference to an immutable result expression tree.
+typedef scoped_refptr<const internal::ResultExprImpl> ResultExpr;
+
+// BoolExpr is an opaque reference to an immutable boolean expression tree.
+typedef scoped_refptr<const internal::BoolExprImpl> BoolExpr;
+
+// Helper class to make writing policies easier.
+class SANDBOX_EXPORT SandboxBPFDSLPolicy : public SandboxBPFPolicy {
+ public:
+ SandboxBPFDSLPolicy() : SandboxBPFPolicy() {}
+ virtual ~SandboxBPFDSLPolicy() {}
+
+ // User extension point for writing custom sandbox policies.
+ virtual ResultExpr EvaluateSyscall(int sysno) const = 0;
+
+ // Optional overload for specifying alternate behavior for invalid
+ // system calls. The default is to return ENOSYS.
+ virtual ResultExpr InvalidSyscall() const;
+
+ // Override implementations from SandboxBPFPolicy. Marked as FINAL
+ // to prevent mixups with child classes accidentally overloading
+ // these instead of the above methods.
+ virtual ErrorCode EvaluateSyscall(SandboxBPF* sb,
+ int sysno) const OVERRIDE FINAL;
+ virtual ErrorCode InvalidSyscall(SandboxBPF* sb) const OVERRIDE FINAL;
+
+ // Helper method so policies can just write Trap(func, aux).
+ static ResultExpr Trap(::sandbox::Trap::TrapFnc trap_func, void* aux);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SandboxBPFDSLPolicy);
+};
+
+// Allow specifies a result that the system call should be allowed to
+// execute normally.
+SANDBOX_EXPORT ResultExpr Allow();
+
+// Error specifies a result that the system call should fail with
+// error number |err|. As a special case, Error(0) will result in the
+// system call appearing to have succeeded, but without having any
+// side effects.
+SANDBOX_EXPORT ResultExpr Error(int err);
+
+// Trap specifies a result that the system call should be handled by
+// trapping back into userspace and invoking |trap_func|, passing
+// |aux| as the second parameter.
+SANDBOX_EXPORT ResultExpr Trap(::sandbox::Trap::TrapFnc trap_func, void* aux);
+
+template <typename T>
+class SANDBOX_EXPORT Arg {
+ public:
+ // Initializes the Arg to represent the |num|th system call
+ // argument (indexed from 0), which is of type |T|.
+ explicit Arg(int num) : num_(num), mask_(-1) {}
+
+ Arg(const Arg& arg) : num_(arg.num_), mask_(arg.mask_) {}
+
+ // Returns an Arg representing the current argument, but after
+ // bitwise-and'ing it with |rhs|.
+ Arg operator&(uint64_t rhs) const { return Arg(num_, mask_ & rhs); }
+
+ // Returns a boolean expression comparing whether the system call
+ // argument (after applying any bitmasks, if appropriate) equals |rhs|.
+ BoolExpr operator==(T rhs) const;
+
+ private:
+ Arg(int num, uint64_t mask) : num_(num), mask_(mask) {}
+ int num_;
+ uint64_t mask_;
+ DISALLOW_ASSIGN(Arg);
+};
+
+// Various ways to combine boolean expressions into more complex expressions.
+// They follow standard boolean algebra laws.
+SANDBOX_EXPORT BoolExpr operator!(BoolExpr cond);
+SANDBOX_EXPORT BoolExpr operator&&(BoolExpr lhs, BoolExpr rhs);
+SANDBOX_EXPORT BoolExpr operator||(BoolExpr lhs, BoolExpr rhs);
+
+// If begins a conditional result expression predicated on the
+// specified boolean expression.
+SANDBOX_EXPORT Elser If(BoolExpr cond, ResultExpr then_result);
+
+class SANDBOX_EXPORT Elser {
+ public:
+ Elser(const Elser& elser);
+ ~Elser();
+
+ // ElseIf extends the conditional result expression with another
+ // "if then" clause, predicated on the specified boolean expression.
+ Elser ElseIf(BoolExpr cond, ResultExpr then_result) const;
+
+ // Else terminates a conditional result expression using |else_result| as
+ // the default fallback result expression.
+ ResultExpr Else(ResultExpr else_result) const;
+
+ private:
+ typedef std::pair<BoolExpr, ResultExpr> Clause;
+ explicit Elser(Cons<Clause>::List clause_list);
+ Cons<Clause>::List clause_list_;
+ friend Elser If(BoolExpr, ResultExpr);
+ DISALLOW_ASSIGN(Elser);
+};
+
+// =====================================================================
+// Official API ends here.
+// =====================================================================
+
+// Definitions below are necessary here only for C++03 compatibility.
+// Once C++11 is available, they should be moved into bpf_dsl.cc via extern
+// templates.
+namespace internal {
+
+// Returns a boolean expression that represents whether system call
+// argument |num| of size |size| is equal to |val|, when masked
+// according to |mask|. Users should use the Arg template class below
+// instead of using this API directly.
+SANDBOX_EXPORT BoolExpr
+ ArgEq(int num, size_t size, uint64_t mask, uint64_t val);
+
+// Internal interface implemented by BoolExpr implementations.
+class SANDBOX_EXPORT BoolExprImpl : public base::RefCounted<BoolExprImpl> {
+ public:
+ BoolExprImpl() {}
+ virtual ErrorCode Compile(SandboxBPF* sb,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const = 0;
+
+ protected:
+ virtual ~BoolExprImpl() {}
+
+ private:
+ friend class base::RefCounted<BoolExprImpl>;
+ DISALLOW_COPY_AND_ASSIGN(BoolExprImpl);
+};
+
+// Internal interface implemented by ResultExpr implementations.
+class SANDBOX_EXPORT ResultExprImpl : public base::RefCounted<ResultExprImpl> {
+ public:
+ ResultExprImpl() {}
+ virtual ErrorCode Compile(SandboxBPF* sb) const = 0;
+
+ protected:
+ virtual ~ResultExprImpl() {}
+
+ private:
+ friend class base::RefCounted<ResultExprImpl>;
+ DISALLOW_COPY_AND_ASSIGN(ResultExprImpl);
+};
+
+} // namespace internal
+
+// Definition requires ArgEq to have been declared. Moved out-of-line
+// to minimize how much internal clutter users have to ignore while
+// reading the header documentation.
+template <typename T>
+BoolExpr Arg<T>::operator==(T rhs) const {
+ return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint64_t>(rhs));
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc b/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc
new file mode 100644
index 0000000..560d94c
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc
@@ -0,0 +1,268 @@
+// Copyright 2014 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/bpf_dsl/bpf_dsl.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
+using namespace sandbox::bpf_dsl;
+
+// Helper macro to assert that invoking system call |sys| directly via
+// Syscall::Call with arguments |...| returns |res|.
+// Errors can be asserted by specifying a value like "-EINVAL".
+#define ASSERT_SYSCALL_RESULT(res, sys, ...) \
+ BPF_ASSERT_EQ(res, Stubs::sys(__VA_ARGS__))
+
+namespace sandbox {
+namespace {
+
+// Type safe stubs for tested system calls.
+class Stubs {
+ public:
+ static int getpgid(pid_t pid) { return Syscall::Call(__NR_getpgid, pid); }
+ static int setuid(uid_t uid) { return Syscall::Call(__NR_setuid, uid); }
+ static int setgid(gid_t gid) { return Syscall::Call(__NR_setgid, gid); }
+
+ static int uname(struct utsname* buf) {
+ return Syscall::Call(__NR_uname, buf);
+ }
+
+ static int setresuid(uid_t ruid, uid_t euid, uid_t suid) {
+ return Syscall::Call(__NR_setresuid, ruid, euid, suid);
+ }
+
+#if !defined(ARCH_CPU_X86)
+ static int socketpair(int domain, int type, int protocol, int sv[2]) {
+ return Syscall::Call(__NR_socketpair, domain, type, protocol, sv);
+ }
+#endif
+};
+
+class BasicPolicy : public SandboxBPFDSLPolicy {
+ public:
+ BasicPolicy() {}
+ virtual ~BasicPolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
+ if (sysno == __NR_getpgid) {
+ const Arg<pid_t> pid(0);
+ return If(pid == 0, Error(EPERM)).Else(Error(EINVAL));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicPolicy);
+};
+
+BPF_TEST_C(BPFDSL, Basic, BasicPolicy) {
+ ASSERT_SYSCALL_RESULT(-EPERM, getpgid, 0);
+ ASSERT_SYSCALL_RESULT(-EINVAL, getpgid, 1);
+}
+
+/* On IA-32, socketpair() is implemented via socketcall(). :-( */
+#if !defined(ARCH_CPU_X86)
+class BooleanLogicPolicy : public SandboxBPFDSLPolicy {
+ public:
+ BooleanLogicPolicy() {}
+ virtual ~BooleanLogicPolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
+ if (sysno == __NR_socketpair) {
+ const Arg<int> domain(0), type(1), protocol(2);
+ return If(domain == AF_UNIX &&
+ (type == SOCK_STREAM || type == SOCK_DGRAM) &&
+ protocol == 0,
+ Error(EPERM)).Else(Error(EINVAL));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BooleanLogicPolicy);
+};
+
+BPF_TEST_C(BPFDSL, BooleanLogic, BooleanLogicPolicy) {
+ int sv[2];
+
+ // Acceptable combinations that should return EPERM.
+ ASSERT_SYSCALL_RESULT(-EPERM, socketpair, AF_UNIX, SOCK_STREAM, 0, sv);
+ ASSERT_SYSCALL_RESULT(-EPERM, socketpair, AF_UNIX, SOCK_DGRAM, 0, sv);
+
+ // Combinations that are invalid for only one reason; should return EINVAL.
+ ASSERT_SYSCALL_RESULT(-EINVAL, socketpair, AF_INET, SOCK_STREAM, 0, sv);
+ ASSERT_SYSCALL_RESULT(-EINVAL, socketpair, AF_UNIX, SOCK_SEQPACKET, 0, sv);
+ ASSERT_SYSCALL_RESULT(
+ -EINVAL, socketpair, AF_UNIX, SOCK_STREAM, IPPROTO_TCP, sv);
+
+ // Completely unacceptable combination; should also return EINVAL.
+ ASSERT_SYSCALL_RESULT(
+ -EINVAL, socketpair, AF_INET, SOCK_SEQPACKET, IPPROTO_UDP, sv);
+}
+#endif // !ARCH_CPU_X86
+
+class MoreBooleanLogicPolicy : public SandboxBPFDSLPolicy {
+ public:
+ MoreBooleanLogicPolicy() {}
+ virtual ~MoreBooleanLogicPolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
+ if (sysno == __NR_setresuid) {
+ const Arg<uid_t> ruid(0), euid(1), suid(2);
+ return If(ruid == 0 || euid == 0 || suid == 0, Error(EPERM))
+ .ElseIf(ruid == 1 && euid == 1 && suid == 1, Error(EAGAIN))
+ .Else(Error(EINVAL));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MoreBooleanLogicPolicy);
+};
+
+BPF_TEST_C(BPFDSL, MoreBooleanLogic, MoreBooleanLogicPolicy) {
+ // Expect EPERM if any set to 0.
+ ASSERT_SYSCALL_RESULT(-EPERM, setresuid, 0, 5, 5);
+ ASSERT_SYSCALL_RESULT(-EPERM, setresuid, 5, 0, 5);
+ ASSERT_SYSCALL_RESULT(-EPERM, setresuid, 5, 5, 0);
+
+ // Expect EAGAIN if all set to 1.
+ ASSERT_SYSCALL_RESULT(-EAGAIN, setresuid, 1, 1, 1);
+
+ // Expect EINVAL for anything else.
+ ASSERT_SYSCALL_RESULT(-EINVAL, setresuid, 5, 1, 1);
+ ASSERT_SYSCALL_RESULT(-EINVAL, setresuid, 1, 5, 1);
+ ASSERT_SYSCALL_RESULT(-EINVAL, setresuid, 1, 1, 5);
+ ASSERT_SYSCALL_RESULT(-EINVAL, setresuid, 3, 4, 5);
+}
+
+static const uintptr_t kDeadBeefAddr =
+ static_cast<uintptr_t>(0xdeadbeefdeadbeefULL);
+
+class ArgSizePolicy : public SandboxBPFDSLPolicy {
+ public:
+ ArgSizePolicy() {}
+ virtual ~ArgSizePolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
+ if (sysno == __NR_uname) {
+ const Arg<uintptr_t> addr(0);
+ return If(addr == kDeadBeefAddr, Error(EPERM)).Else(Allow());
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ArgSizePolicy);
+};
+
+BPF_TEST_C(BPFDSL, ArgSizeTest, ArgSizePolicy) {
+ struct utsname buf;
+ ASSERT_SYSCALL_RESULT(0, uname, &buf);
+ ASSERT_SYSCALL_RESULT(
+ -EPERM, uname, reinterpret_cast<struct utsname*>(kDeadBeefAddr));
+}
+
+class TrappingPolicy : public SandboxBPFDSLPolicy {
+ public:
+ TrappingPolicy() {}
+ virtual ~TrappingPolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
+ if (sysno == __NR_uname) {
+ return Trap(UnameTrap, &count_);
+ }
+ return Allow();
+ }
+
+ private:
+ static intptr_t count_;
+
+ static intptr_t UnameTrap(const struct arch_seccomp_data& data, void* aux) {
+ BPF_ASSERT_EQ(&count_, aux);
+ return ++count_;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(TrappingPolicy);
+};
+
+intptr_t TrappingPolicy::count_;
+
+BPF_TEST_C(BPFDSL, TrapTest, TrappingPolicy) {
+ ASSERT_SYSCALL_RESULT(1, uname, NULL);
+ ASSERT_SYSCALL_RESULT(2, uname, NULL);
+ ASSERT_SYSCALL_RESULT(3, uname, NULL);
+}
+
+class MaskingPolicy : public SandboxBPFDSLPolicy {
+ public:
+ MaskingPolicy() {}
+ virtual ~MaskingPolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
+ if (sysno == __NR_setuid) {
+ const Arg<uid_t> uid(0);
+ return If((uid & 0xf) == 0, Error(EINVAL)).Else(Error(EACCES));
+ }
+ if (sysno == __NR_setgid) {
+ const Arg<gid_t> gid(0);
+ return If((gid & 0xf0) == 0xf0, Error(EINVAL)).Else(Error(EACCES));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MaskingPolicy);
+};
+
+BPF_TEST_C(BPFDSL, MaskTest, MaskingPolicy) {
+ for (uid_t uid = 0; uid < 0x100; ++uid) {
+ const int expect_errno = (uid & 0xf) == 0 ? EINVAL : EACCES;
+ ASSERT_SYSCALL_RESULT(-expect_errno, setuid, uid);
+ }
+
+ for (gid_t gid = 0; gid < 0x100; ++gid) {
+ const int expect_errno = (gid & 0xf0) == 0xf0 ? EINVAL : EACCES;
+ ASSERT_SYSCALL_RESULT(-expect_errno, setgid, gid);
+ }
+}
+
+class ElseIfPolicy : public SandboxBPFDSLPolicy {
+ public:
+ ElseIfPolicy() {}
+ virtual ~ElseIfPolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
+ if (sysno == __NR_setuid) {
+ const Arg<uid_t> uid(0);
+ return If((uid & 0xfff) == 0, Error(0))
+ .ElseIf((uid & 0xff0) == 0, Error(EINVAL))
+ .ElseIf((uid & 0xf00) == 0, Error(EEXIST))
+ .Else(Error(EACCES));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ElseIfPolicy);
+};
+
+BPF_TEST_C(BPFDSL, ElseIfTest, ElseIfPolicy) {
+ ASSERT_SYSCALL_RESULT(0, setuid, 0);
+
+ ASSERT_SYSCALL_RESULT(-EINVAL, setuid, 0x0001);
+ ASSERT_SYSCALL_RESULT(-EINVAL, setuid, 0x0002);
+
+ ASSERT_SYSCALL_RESULT(-EEXIST, setuid, 0x0011);
+ ASSERT_SYSCALL_RESULT(-EEXIST, setuid, 0x0022);
+
+ ASSERT_SYSCALL_RESULT(-EACCES, setuid, 0x0111);
+ ASSERT_SYSCALL_RESULT(-EACCES, setuid, 0x0222);
+}
+
+} // namespace
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/cons.h b/sandbox/linux/bpf_dsl/cons.h
new file mode 100644
index 0000000..eb9e3aa
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/cons.h
@@ -0,0 +1,46 @@
+// Copyright 2014 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_BPF_DSL_CONS_H_
+#define SANDBOX_LINUX_BPF_DSL_CONS_H_
+
+#include "base/memory/ref_counted.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Cons provides an immutable linked list abstraction as commonly
+// provided in functional programming languages like Lisp or Haskell.
+template <typename T>
+class Cons : public base::RefCounted<Cons<T> > {
+ public:
+ // List provides an abstraction for referencing a list of zero or
+ // more Cons nodes.
+ typedef scoped_refptr<const Cons<T> > List;
+
+ // Return this node's head element.
+ const T& head() const { return head_; }
+
+ // Return this node's tail element.
+ List tail() const { return tail_; }
+
+ // Construct a new List using |head| and |tail|.
+ static List Make(const T& head, List tail) {
+ return make_scoped_refptr(new const Cons<T>(head, tail));
+ }
+
+ private:
+ Cons(const T& head, List tail) : head_(head), tail_(tail) {}
+ virtual ~Cons() {}
+
+ T head_;
+ List tail_;
+
+ friend class base::RefCounted<Cons<T> >;
+ DISALLOW_COPY_AND_ASSIGN(Cons);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_CONS_H_
diff --git a/sandbox/linux/bpf_dsl/cons_unittest.cc b/sandbox/linux/bpf_dsl/cons_unittest.cc
new file mode 100644
index 0000000..790cae0
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/cons_unittest.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 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/bpf_dsl/cons.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+namespace {
+
+std::string Join(Cons<char>::List char_list) {
+ std::string res;
+ for (Cons<char>::List it = char_list; it; it = it->tail()) {
+ res.push_back(it->head());
+ }
+ return res;
+}
+
+TEST(ConsListTest, Basic) {
+ Cons<char>::List ba =
+ Cons<char>::Make('b', Cons<char>::Make('a', Cons<char>::List()));
+ EXPECT_EQ("ba", Join(ba));
+
+ Cons<char>::List cba = Cons<char>::Make('c', ba);
+ Cons<char>::List dba = Cons<char>::Make('d', ba);
+ EXPECT_EQ("cba", Join(cba));
+ EXPECT_EQ("dba", Join(dba));
+}
+
+} // namespace
+} // namespace sandbox
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi
index 9ddcf0c..274d065 100644
--- a/sandbox/linux/sandbox_linux.gypi
+++ b/sandbox/linux/sandbox_linux.gypi
@@ -117,6 +117,9 @@
'target_name': 'seccomp_bpf',
'type': '<(component)',
'sources': [
+ 'bpf_dsl/bpf_dsl.cc',
+ 'bpf_dsl/bpf_dsl.h',
+ 'bpf_dsl/cons.h',
'seccomp-bpf/basicblock.cc',
'seccomp-bpf/basicblock.h',
'seccomp-bpf/codegen.cc',
diff --git a/sandbox/linux/sandbox_linux_test_sources.gypi b/sandbox/linux/sandbox_linux_test_sources.gypi
index c72ef51..c2f365b 100644
--- a/sandbox/linux/sandbox_linux_test_sources.gypi
+++ b/sandbox/linux/sandbox_linux_test_sources.gypi
@@ -34,6 +34,8 @@
}],
[ 'use_seccomp_bpf==1', {
'sources': [
+ 'bpf_dsl/bpf_dsl_unittest.cc',
+ 'bpf_dsl/cons_unittest.cc',
'seccomp-bpf-helpers/baseline_policy_unittest.cc',
'seccomp-bpf/bpf_tests_unittest.cc',
'seccomp-bpf/codegen_unittest.cc',