summaryrefslogtreecommitdiffstats
path: root/sandbox
diff options
context:
space:
mode:
authormarkus@chromium.org <markus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-26 01:39:39 +0000
committermarkus@chromium.org <markus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-26 01:39:39 +0000
commit083fe1e0bc4ead809af46a75fbb979cc95e7491d (patch)
treed4a47bc54dc91671fc917968e7ad5ed6203b474b /sandbox
parent3124fbfa6a99ad601c71bb2edf403ddb5cdf080b (diff)
downloadchromium_src-083fe1e0bc4ead809af46a75fbb979cc95e7491d.zip
chromium_src-083fe1e0bc4ead809af46a75fbb979cc95e7491d.tar.gz
chromium_src-083fe1e0bc4ead809af46a75fbb979cc95e7491d.tar.bz2
SECCOMP-BPF: Refactor the BPF sandbox API to use objects rather than "static" methods.
This change allows us to stack multiple instances of the sandbox. Also, split up headers in a generally saner fashion. BUG=130662 TEST=sandbox_linux_unittests Review URL: https://chromiumcodereview.appspot.com/12223109 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@184541 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r--sandbox/linux/sandbox_linux.gypi2
-rw-r--r--sandbox/linux/seccomp-bpf/Makefile2
-rw-r--r--sandbox/linux/seccomp-bpf/bpf_tests.h16
-rw-r--r--sandbox/linux/seccomp-bpf/codegen.cc2
-rw-r--r--sandbox/linux/seccomp-bpf/demo.cc143
-rw-r--r--sandbox/linux/seccomp-bpf/die.cc5
-rw-r--r--sandbox/linux/seccomp-bpf/die.h3
-rw-r--r--sandbox/linux/seccomp-bpf/errorcode.cc3
-rw-r--r--sandbox/linux/seccomp-bpf/errorcode.h20
-rw-r--r--sandbox/linux/seccomp-bpf/errorcode_unittest.cc24
-rw-r--r--sandbox/linux/seccomp-bpf/linux_seccomp.h197
-rw-r--r--sandbox/linux/seccomp-bpf/port.h36
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf.cc384
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf.h373
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc255
-rw-r--r--sandbox/linux/seccomp-bpf/syscall.cc2
-rw-r--r--sandbox/linux/seccomp-bpf/syscall_iterator.cc3
-rw-r--r--sandbox/linux/seccomp-bpf/syscall_unittest.cc4
-rw-r--r--sandbox/linux/seccomp-bpf/trap.cc2
-rw-r--r--sandbox/linux/seccomp-bpf/trap.h6
-rw-r--r--sandbox/linux/seccomp-bpf/util.cc164
-rw-r--r--sandbox/linux/seccomp-bpf/util.h19
-rw-r--r--sandbox/linux/seccomp-bpf/verifier.cc357
-rw-r--r--sandbox/linux/seccomp-bpf/verifier.h39
24 files changed, 1071 insertions, 990 deletions
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi
index ae7769f..5bfa6f7 100644
--- a/sandbox/linux/sandbox_linux.gypi
+++ b/sandbox/linux/sandbox_linux.gypi
@@ -101,6 +101,8 @@
'seccomp-bpf/errorcode.cc',
'seccomp-bpf/errorcode.h',
'seccomp-bpf/instruction.h',
+ 'seccomp-bpf/linux_seccomp.h',
+ 'seccomp-bpf/port.h',
'seccomp-bpf/sandbox_bpf.cc',
'seccomp-bpf/sandbox_bpf.h',
'seccomp-bpf/syscall.cc',
diff --git a/sandbox/linux/seccomp-bpf/Makefile b/sandbox/linux/seccomp-bpf/Makefile
index b7c1f9d..6b35580 100644
--- a/sandbox/linux/seccomp-bpf/Makefile
+++ b/sandbox/linux/seccomp-bpf/Makefile
@@ -2,7 +2,7 @@ DEF_CFLAGS = -g -O3 -Wall -Werror -Wextra -Wno-missing-field-initializers -fPIC
DEF_CPPFLAGS = -D_GNU_SOURCE -DSECCOMP_BPF_STANDALONE -iquote ../../..
DEF_LDFLAGS = -g -lpthread
DEPFLAGS = -MMD -MF .$@.d
-MODS := demo sandbox_bpf basicblock codegen die errorcode syscall syscall_iterator trap util verifier
+MODS := demo sandbox_bpf basicblock codegen die errorcode syscall syscall_iterator trap verifier
OBJS64 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o64/')
OBJS32 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o32/')
ALL_OBJS = $(OBJS32) $(OBJS64)
diff --git a/sandbox/linux/seccomp-bpf/bpf_tests.h b/sandbox/linux/seccomp-bpf/bpf_tests.h
index d631428..680ece601 100644
--- a/sandbox/linux/seccomp-bpf/bpf_tests.h
+++ b/sandbox/linux/seccomp-bpf/bpf_tests.h
@@ -5,6 +5,10 @@
#ifndef SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
#define SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
#include "sandbox/linux/tests/unit_tests.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
@@ -81,17 +85,19 @@ class BpfTests : public UnitTests {
playground2::Sandbox::STATUS_AVAILABLE);
// Initialize and then start the sandbox with our custom policy
- playground2::Sandbox::set_proc_fd(proc_fd);
- playground2::Sandbox::SetSandboxPolicy(arg->policy(), &arg->aux_);
- playground2::Sandbox::StartSandbox();
+ playground2::Sandbox sandbox;
+ sandbox.set_proc_fd(proc_fd);
+ sandbox.SetSandboxPolicy(arg->policy(), &arg->aux_);
+ sandbox.Sandbox::StartSandbox();
arg->test()(arg->aux_);
} else {
// Call the compiler and verify the policy. That's the least we can do,
// if we don't have kernel support.
- playground2::Sandbox::SetSandboxPolicy(arg->policy(), &arg->aux_);
+ playground2::Sandbox sandbox;
+ sandbox.SetSandboxPolicy(arg->policy(), &arg->aux_);
playground2::Sandbox::Program *program =
- playground2::Sandbox::AssembleFilter(true /* force_verification */);
+ sandbox.AssembleFilter(true /* force_verification */);
delete program;
sandbox::UnitTests::IgnoreThisTest();
}
diff --git a/sandbox/linux/seccomp-bpf/codegen.cc b/sandbox/linux/seccomp-bpf/codegen.cc
index 6528990..17b5d84 100644
--- a/sandbox/linux/seccomp-bpf/codegen.cc
+++ b/sandbox/linux/seccomp-bpf/codegen.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <stdio.h>
+
#include "sandbox/linux/seccomp-bpf/codegen.h"
diff --git a/sandbox/linux/seccomp-bpf/demo.cc b/sandbox/linux/seccomp-bpf/demo.cc
index fcae399..b2622ec45 100644
--- a/sandbox/linux/seccomp-bpf/demo.cc
+++ b/sandbox/linux/seccomp-bpf/demo.cc
@@ -10,6 +10,7 @@
#include <netinet/udp.h>
#include <pthread.h>
#include <signal.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -26,12 +27,10 @@
#include <unistd.h>
#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;
#define ERR EPERM
@@ -41,6 +40,106 @@ using playground2::Util;
// actually enforce restrictions in a meaningful way:
#define _exit(x) do { } while (0)
+namespace {
+
+bool SendFds(int transport, const void *buf, size_t len, ...) {
+ int count = 0;
+ va_list ap;
+ va_start(ap, len);
+ while (va_arg(ap, int) >= 0) {
+ ++count;
+ }
+ va_end(ap);
+ if (!count) {
+ return false;
+ }
+ char cmsg_buf[CMSG_SPACE(count*sizeof(int))];
+ memset(cmsg_buf, 0, sizeof(cmsg_buf));
+ struct iovec iov[2] = { { 0 } };
+ struct msghdr msg = { 0 };
+ int dummy = 0;
+ iov[0].iov_base = &dummy;
+ iov[0].iov_len = sizeof(dummy);
+ if (buf && len > 0) {
+ iov[1].iov_base = const_cast<void *>(buf);
+ iov[1].iov_len = len;
+ }
+ msg.msg_iov = iov;
+ msg.msg_iovlen = (buf && len > 0) ? 2 : 1;
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = CMSG_LEN(count*sizeof(int));
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(count*sizeof(int));
+ va_start(ap, len);
+ for (int i = 0, fd; (fd = va_arg(ap, int)) >= 0; ++i) {
+ (reinterpret_cast<int *>(CMSG_DATA(cmsg)))[i] = fd;
+ }
+ return sendmsg(transport, &msg, 0) ==
+ static_cast<ssize_t>(sizeof(dummy) + ((buf && len > 0) ? len : 0));
+}
+
+bool GetFds(int transport, void *buf, size_t *len, ...) {
+ int count = 0;
+ va_list ap;
+ va_start(ap, len);
+ for (int *fd; (fd = va_arg(ap, int *)) != NULL; ++count) {
+ *fd = -1;
+ }
+ va_end(ap);
+ if (!count) {
+ return false;
+ }
+ char cmsg_buf[CMSG_SPACE(count*sizeof(int))];
+ memset(cmsg_buf, 0, sizeof(cmsg_buf));
+ struct iovec iov[2] = { { 0 } };
+ struct msghdr msg = { 0 };
+ int err;
+ iov[0].iov_base = &err;
+ iov[0].iov_len = sizeof(int);
+ if (buf && len && *len > 0) {
+ iov[1].iov_base = buf;
+ iov[1].iov_len = *len;
+ }
+ msg.msg_iov = iov;
+ msg.msg_iovlen = (buf && len && *len > 0) ? 2 : 1;
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = CMSG_LEN(count*sizeof(int));
+ ssize_t bytes = recvmsg(transport, &msg, 0);
+ if (len) {
+ *len = bytes > static_cast<int>(sizeof(int)) ? bytes - sizeof(int) : 0;
+ }
+ if (bytes != static_cast<ssize_t>(sizeof(int) + iov[1].iov_len)) {
+ if (bytes >= 0) {
+ errno = 0;
+ }
+ return false;
+ }
+ if (err) {
+ // "err" is the first four bytes of the payload. If these are non-zero,
+ // the sender on the other side of the socketpair sent us an errno value.
+ // We don't expect to get any file handles in this case.
+ errno = err;
+ return false;
+ }
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+ if ((msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) ||
+ !cmsg ||
+ cmsg->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_RIGHTS ||
+ cmsg->cmsg_len != CMSG_LEN(count*sizeof(int))) {
+ errno = EBADF;
+ return false;
+ }
+ va_start(ap, len);
+ for (int *fd, i = 0; (fd = va_arg(ap, int *)) != NULL; ++i) {
+ *fd = (reinterpret_cast<int *>(CMSG_DATA(cmsg)))[i];
+ }
+ va_end(ap);
+ return true;
+}
+
// POSIX doesn't define any async-signal safe function for converting
// an integer to ASCII. We'll have to define our own version.
@@ -48,7 +147,7 @@ using playground2::Util;
// conversion was successful or NULL otherwise. It never writes more than "sz"
// bytes. Output will be truncated as needed, and a NUL character is always
// appended.
-static char *itoa_r(int i, char *buf, size_t sz) {
+char *itoa_r(int i, char *buf, size_t sz) {
// Make sure we can write at least one NUL byte.
size_t n = 1;
if (n > sz) {
@@ -116,8 +215,7 @@ static char *itoa_r(int i, char *buf, size_t sz) {
// might try to evaluate the system call in user-space, instead.
// The only notable complication is that this function must be async-signal
// safe. This restricts the libary functions that we can call.
-static intptr_t defaultHandler(const struct arch_seccomp_data& data,
- void *) {
+intptr_t DefaultHandler(const struct arch_seccomp_data& data, void *) {
static const char msg0[] = "Disallowed system call #";
static const char msg1[] = "\n";
char buf[sizeof(msg0) - 1 + 25 + sizeof(msg1)];
@@ -137,7 +235,7 @@ static intptr_t defaultHandler(const struct arch_seccomp_data& data,
return -ERR;
}
-static ErrorCode evaluator(int sysno, void *) {
+ErrorCode Evaluator(Sandbox *sandbox, int sysno, void *) {
switch (sysno) {
#if defined(__NR_accept)
case __NR_accept: case __NR_accept4:
@@ -226,13 +324,13 @@ static ErrorCode evaluator(int sysno, void *) {
case __NR_prctl:
// Allow PR_SET_DUMPABLE and PR_GET_DUMPABLE. Do not allow anything else.
- return Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
PR_SET_DUMPABLE,
ErrorCode(ErrorCode::ERR_ALLOWED),
- Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
PR_GET_DUMPABLE,
ErrorCode(ErrorCode::ERR_ALLOWED),
- Sandbox::Trap(defaultHandler, NULL)));
+ sandbox->Trap(DefaultHandler, NULL)));
// The following system calls are temporarily permitted. This must be
// tightened later. But we currently don't implement enough of the sandboxing
@@ -267,15 +365,15 @@ static ErrorCode evaluator(int sysno, void *) {
// Everything that isn't explicitly allowed is denied.
default:
- return Sandbox::Trap(defaultHandler, NULL);
+ return sandbox->Trap(DefaultHandler, NULL);
}
}
-static void *threadFnc(void *arg) {
+void *ThreadFnc(void *arg) {
return arg;
}
-static void *sendmsgStressThreadFnc(void *arg) {
+void *SendmsgStressThreadFnc(void *arg) {
if (arg) { }
static const int repetitions = 100;
static const int kNumFds = 3;
@@ -287,8 +385,8 @@ static void *sendmsgStressThreadFnc(void *arg) {
}
size_t len = 4;
char buf[4];
- if (!Util::SendFds(fds[0], "test", 4, fds[1], fds[1], fds[1], -1) ||
- !Util::GetFds(fds[1], buf, &len, fds+2, fds+3, fds+4, NULL) ||
+ if (!SendFds(fds[0], "test", 4, fds[1], fds[1], fds[1], -1) ||
+ !GetFds(fds[1], buf, &len, fds+2, fds+3, fds+4, NULL) ||
len != 4 ||
memcmp(buf, "test", len) ||
write(fds[2], "demo", 4) != 4 ||
@@ -307,6 +405,8 @@ static void *sendmsgStressThreadFnc(void *arg) {
return NULL;
}
+} // namespace
+
int main(int argc, char *argv[]) {
if (argc) { }
if (argv) { }
@@ -316,13 +416,14 @@ int main(int argc, char *argv[]) {
perror("sandbox");
_exit(1);
}
- Sandbox::set_proc_fd(proc_fd);
- Sandbox::SetSandboxPolicy(evaluator, NULL);
- Sandbox::StartSandbox();
+ Sandbox sandbox;
+ sandbox.set_proc_fd(proc_fd);
+ sandbox.SetSandboxPolicy(Evaluator, NULL);
+ sandbox.StartSandbox();
// Check that we can create threads
pthread_t thr;
- if (!pthread_create(&thr, NULL, threadFnc,
+ if (!pthread_create(&thr, NULL, ThreadFnc,
reinterpret_cast<void *>(0x1234))) {
void *ret;
pthread_join(thr, &ret);
@@ -376,8 +477,8 @@ int main(int argc, char *argv[]) {
}
size_t len = 4;
char buf[4];
- if (!Util::SendFds(fds[0], "test", 4, fds[1], -1) ||
- !Util::GetFds(fds[1], buf, &len, fds+2, NULL) ||
+ if (!SendFds(fds[0], "test", 4, fds[1], -1) ||
+ !GetFds(fds[1], buf, &len, fds+2, NULL) ||
len != 4 ||
memcmp(buf, "test", len) ||
write(fds[2], "demo", 4) != 4 ||
@@ -410,7 +511,7 @@ int main(int argc, char *argv[]) {
pthread_t sendmsgStressThreads[kSendmsgStressNumThreads];
for (int i = 0; i < kSendmsgStressNumThreads; ++i) {
if (pthread_create(sendmsgStressThreads + i, NULL,
- sendmsgStressThreadFnc, NULL)) {
+ SendmsgStressThreadFnc, NULL)) {
perror("pthread_create");
_exit(1);
}
diff --git a/sandbox/linux/seccomp-bpf/die.cc b/sandbox/linux/seccomp-bpf/die.cc
index 92ffa2a..4962c4d 100644
--- a/sandbox/linux/seccomp-bpf/die.cc
+++ b/sandbox/linux/seccomp-bpf/die.cc
@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <errno.h>
+#include <linux/unistd.h>
+#include <stdio.h>
+#include <sys/prctl.h>
+
#include <string>
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
diff --git a/sandbox/linux/seccomp-bpf/die.h b/sandbox/linux/seccomp-bpf/die.h
index cecb506..f15f108 100644
--- a/sandbox/linux/seccomp-bpf/die.h
+++ b/sandbox/linux/seccomp-bpf/die.h
@@ -5,6 +5,9 @@
#ifndef SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
#define SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
+#include "sandbox/linux/seccomp-bpf/port.h"
+
+
namespace playground2 {
class Die {
diff --git a/sandbox/linux/seccomp-bpf/errorcode.cc b/sandbox/linux/seccomp-bpf/errorcode.cc
index c9d4988..ab89d73 100644
--- a/sandbox/linux/seccomp-bpf/errorcode.cc
+++ b/sandbox/linux/seccomp-bpf/errorcode.cc
@@ -2,7 +2,8 @@
// 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/die.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
namespace playground2 {
diff --git a/sandbox/linux/seccomp-bpf/errorcode.h b/sandbox/linux/seccomp-bpf/errorcode.h
index 75efb53..901525c 100644
--- a/sandbox/linux/seccomp-bpf/errorcode.h
+++ b/sandbox/linux/seccomp-bpf/errorcode.h
@@ -5,6 +5,7 @@
#ifndef SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__
#define SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
#include "sandbox/linux/seccomp-bpf/trap.h"
namespace playground2 {
@@ -95,6 +96,10 @@ class ErrorCode {
OP_NUM_OPS,
};
+ enum ErrorType {
+ ET_INVALID, ET_SIMPLE, ET_TRAP, ET_COND,
+ };
+
// 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
@@ -120,6 +125,16 @@ class ErrorCode {
bool LessThan(const ErrorCode& err) const;
uint32_t err() const { return err_; }
+ ErrorType error_type() const { return error_type_; }
+
+ bool safe() const { return safe_; }
+
+ uint64_t value() const { return value_; }
+ int argno() const { return argno_; }
+ ArgType width() const { return width_; }
+ Operation op() const { return op_; }
+ const ErrorCode *passed() const { return passed_; }
+ const ErrorCode *failed() const { return failed_; }
struct LessThan {
bool operator()(const ErrorCode& a, const ErrorCode& b) const {
@@ -131,11 +146,6 @@ class ErrorCode {
friend class CodeGen;
friend class Sandbox;
friend class Trap;
- 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
diff --git a/sandbox/linux/seccomp-bpf/errorcode_unittest.cc b/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
index 21f889e..10f2132 100644
--- a/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
@@ -19,17 +19,19 @@ SANDBOX_TEST(ErrorCode, ErrnoConstructor) {
ErrorCode e2(EPERM);
SANDBOX_ASSERT(e2.err() == SECCOMP_RET_ERRNO + EPERM);
- ErrorCode e3 = Sandbox::Trap(NULL, NULL);
+ Sandbox sandbox;
+ 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 sandbox;
+ 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");
+ ErrorCode e2 = sandbox.Trap(NULL, "a");
SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) ==
(e2.err() & SECCOMP_RET_DATA));
}
@@ -44,9 +46,10 @@ SANDBOX_TEST(ErrorCode, Equals) {
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 sandbox;
+ 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));
@@ -64,9 +67,10 @@ SANDBOX_TEST(ErrorCode, LessThan) {
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 sandbox;
+ 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));
diff --git a/sandbox/linux/seccomp-bpf/linux_seccomp.h b/sandbox/linux/seccomp-bpf/linux_seccomp.h
new file mode 100644
index 0000000..0de0259
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/linux_seccomp.h
@@ -0,0 +1,197 @@
+// 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_LINUX_SECCOMP_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_LINUX_SECCOMP_H__
+
+// 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.
+// If we ever decide that we can now rely on system headers, the following
+// include files should be enabled:
+// #include <linux/audit.h>
+// #include <linux/seccomp.h>
+
+#include <asm/unistd.h>
+#include <linux/filter.h>
+
+// For audit.h
+#ifndef EM_ARM
+#define EM_ARM 40
+#endif
+#ifndef EM_386
+#define EM_386 3
+#endif
+#ifndef EM_X86_64
+#define EM_X86_64 62
+#endif
+
+#ifndef __AUDIT_ARCH_64BIT
+#define __AUDIT_ARCH_64BIT 0x80000000
+#endif
+#ifndef __AUDIT_ARCH_LE
+#define __AUDIT_ARCH_LE 0x40000000
+#endif
+#ifndef AUDIT_ARCH_ARM
+#define AUDIT_ARCH_ARM (EM_ARM|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_I386
+#define AUDIT_ARCH_I386 (EM_386|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_X86_64
+#define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+#endif
+
+// For prctl.h
+#ifndef PR_SET_SECCOMP
+#define PR_SET_SECCOMP 22
+#define PR_GET_SECCOMP 21
+#endif
+#ifndef PR_SET_NO_NEW_PRIVS
+#define PR_SET_NO_NEW_PRIVS 38
+#define PR_GET_NO_NEW_PRIVS 39
+#endif
+#ifndef IPC_64
+#define IPC_64 0x0100
+#endif
+
+#ifndef BPF_MOD
+#define BPF_MOD 0x90
+#endif
+#ifndef BPF_XOR
+#define BPF_XOR 0xA0
+#endif
+
+// In order to build will older tool chains, we currently have to avoid
+// including <linux/seccomp.h>. Until that can be fixed (if ever). Rely on
+// our own definitions of the seccomp kernel ABI.
+#ifndef SECCOMP_MODE_FILTER
+#define SECCOMP_MODE_DISABLED 0
+#define SECCOMP_MODE_STRICT 1
+#define SECCOMP_MODE_FILTER 2 // User user-supplied filter
+#endif
+
+#ifndef SECCOMP_RET_KILL
+// Return values supported for BPF filter programs. Please note that the
+// "illegal" SECCOMP_RET_INVALID is not supported by the kernel, should only
+// ever be used internally, and would result in the kernel killing our process.
+#define SECCOMP_RET_KILL 0x00000000U // Kill the task immediately
+#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
+#define SECCOMP_RET_TRAP 0x00030000U // Disallow and force a SIGSYS
+#define SECCOMP_RET_ERRNO 0x00050000U // Returns an errno
+#define SECCOMP_RET_TRACE 0x7ff00000U // Pass to a tracer or disallow
+#define SECCOMP_RET_ALLOW 0x7fff0000U // Allow
+#define SECCOMP_RET_ACTION 0xffff0000U // Masks for the return value
+#define SECCOMP_RET_DATA 0x0000ffffU // sections
+#else
+#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
+#endif
+
+#ifndef SYS_SECCOMP
+#define SYS_SECCOMP 1
+#endif
+
+// Impose some reasonable maximum BPF program size. Realistically, the
+// kernel probably has much lower limits. But by limiting to less than
+// 30 bits, we can ease requirements on some of our data types.
+#define SECCOMP_MAX_PROGRAM_SIZE (1<<30)
+
+#if defined(__i386__)
+#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)])
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_EAX)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_EAX)
+#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_EIP)
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_EBX)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_ECX)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_EDX)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_ESI)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_EDI)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_EBP)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+#elif defined(__x86_64__)
+#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)])
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_RAX)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_RAX)
+#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_RIP)
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_RDI)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_RSI)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_RDX)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_R10)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_R8)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_R9)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+#elif defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))
+// ARM EABI includes "ARM private" system calls starting at |__ARM_NR_BASE|,
+// 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_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)
+
+#define SECCOMP_ARCH AUDIT_ARCH_ARM
+
+// ARM sigcontext_t is different from i386/x86_64.
+// See </arch/arm/include/asm/sigcontext.h> in the Linux kernel.
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.arm_##_reg)
+// ARM EABI syscall convention.
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, r0)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, r7)
+#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, pc)
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, r0)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, r1)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, r2)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, r3)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, r4)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, r5)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+#else
+#error Unsupported target platform
+
+#endif
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_LINUX_SECCOMP_H__
diff --git a/sandbox/linux/seccomp-bpf/port.h b/sandbox/linux/seccomp-bpf/port.h
new file mode 100644
index 0000000..f10b148
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/port.h
@@ -0,0 +1,36 @@
+// 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.
+
+// Commonly used macro definitions to make the code build in different
+// target environments (e.g. as part of Chrome vs. stand-alone)
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_PORT_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_PORT_H__
+
+#if !defined(SECCOMP_BPF_STANDALONE)
+ #include "base/basictypes.h"
+ #include "base/logging.h"
+ #include "base/posix/eintr_wrapper.h"
+#else
+ #define arraysize(x) (sizeof(x)/sizeof(*(x)))
+
+ #define HANDLE_EINTR TEMP_FAILURE_RETRY
+
+ #define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+ #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+ template <bool>
+ struct CompileAssert {
+ };
+
+ #define COMPILE_ASSERT(expr, msg) \
+ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+#endif
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_PORT_H__
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
index cb688de..1dc5eae 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -8,8 +8,15 @@
#include <sys/cdefs.h>
#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
#include <sys/prctl.h>
+#include <sys/stat.h>
#include <sys/syscall.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
#ifndef SECCOMP_BPF_STANDALONE
#include "base/logging.h"
@@ -24,16 +31,13 @@
namespace {
-void WriteFailedStderrSetupMessage(int out_fd) {
- const char* error_string = strerror(errno);
- static const char msg[] = "You have reproduced a puzzling issue.\n"
- "Please, report to crbug.com/152530!\n"
- "Failed to set up stderr: ";
- if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg)-1)) > 0 && error_string &&
- HANDLE_EINTR(write(out_fd, error_string, strlen(error_string))) > 0 &&
- HANDLE_EINTR(write(out_fd, "\n", 1))) {
- }
-}
+using playground2::ErrorCode;
+using playground2::Instruction;
+using playground2::Sandbox;
+using playground2::Trap;
+using playground2::arch_seccomp_data;
+
+const int kExpectedExitCode = 100;
template<class T> int popcount(T x);
template<> int popcount<unsigned int>(unsigned int x) {
@@ -46,18 +50,21 @@ template<> int popcount<unsigned long long>(unsigned long long x) {
return __builtin_popcountll(x);
}
-} // namespace
-
-// The kernel gives us a sandbox, we turn it into a playground :-)
-// This is version 2 of the playground; version 1 was built on top of
-// pre-BPF seccomp mode.
-namespace playground2 {
-
-const int kExpectedExitCode = 100;
+void WriteFailedStderrSetupMessage(int out_fd) {
+ const char* error_string = strerror(errno);
+ static const char msg[] = "You have reproduced a puzzling issue.\n"
+ "Please, report to crbug.com/152530!\n"
+ "Failed to set up stderr: ";
+ if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg)-1)) > 0 && error_string &&
+ HANDLE_EINTR(write(out_fd, error_string, strlen(error_string))) > 0 &&
+ HANDLE_EINTR(write(out_fd, "\n", 1))) {
+ }
+}
// We define a really simple sandbox policy. It is just good enough for us
// to tell that the sandbox has actually been activated.
-ErrorCode Sandbox::ProbeEvaluator(int sysnum, void *) {
+ErrorCode ProbeEvaluator(Sandbox *, int sysnum, void *) __attribute__((const));
+ErrorCode ProbeEvaluator(Sandbox *, int sysnum, void *) {
switch (sysnum) {
case __NR_getpid:
// Return EPERM so that we can check that the filter actually ran.
@@ -71,24 +78,20 @@ ErrorCode Sandbox::ProbeEvaluator(int sysnum, void *) {
}
}
-void Sandbox::ProbeProcess(void) {
+void ProbeProcess(void) {
if (syscall(__NR_getpid) < 0 && errno == EPERM) {
syscall(__NR_exit_group, static_cast<intptr_t>(kExpectedExitCode));
}
}
-bool Sandbox::IsValidSyscallNumber(int sysnum) {
- return SyscallIterator::IsValid(sysnum);
-}
-
-ErrorCode Sandbox::AllowAllEvaluator(int sysnum, void *) {
- if (!IsValidSyscallNumber(sysnum)) {
+ErrorCode AllowAllEvaluator(Sandbox *, int sysnum, void *) {
+ if (!Sandbox::IsValidSyscallNumber(sysnum)) {
return ErrorCode(ENOSYS);
}
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
-void Sandbox::TryVsyscallProcess(void) {
+void TryVsyscallProcess(void) {
time_t current_time;
// time() is implemented as a vsyscall. With an older glibc, with
// vsyscall=emulate and some versions of the seccomp BPF patch
@@ -98,10 +101,151 @@ void Sandbox::TryVsyscallProcess(void) {
}
}
+bool IsSingleThreaded(int proc_fd) {
+ if (proc_fd < 0) {
+ // Cannot determine whether program is single-threaded. Hope for
+ // the best...
+ return true;
+ }
+
+ struct stat sb;
+ int task = -1;
+ if ((task = openat(proc_fd, "self/task", O_RDONLY|O_DIRECTORY)) < 0 ||
+ fstat(task, &sb) != 0 ||
+ sb.st_nlink != 3 ||
+ HANDLE_EINTR(close(task))) {
+ if (task >= 0) {
+ if (HANDLE_EINTR(close(task))) { }
+ }
+ return false;
+ }
+ return true;
+}
+
+bool 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));
+}
+
+// Function that can be passed as a callback function to CodeGen::Traverse().
+// Checks whether the "insn" returns an UnsafeTrap() ErrorCode. If so, it
+// sets the "bool" variable pointed to by "aux".
+void CheckForUnsafeErrorCodes(Instruction *insn, void *aux) {
+ bool *is_unsafe = static_cast<bool *>(aux);
+ if (!*is_unsafe) {
+ if (BPF_CLASS(insn->code) == BPF_RET &&
+ insn->k > SECCOMP_RET_TRAP &&
+ insn->k - SECCOMP_RET_TRAP <= SECCOMP_RET_DATA) {
+ const ErrorCode& err =
+ Trap::ErrorCodeFromTrapId(insn->k & SECCOMP_RET_DATA);
+ if (err.error_type() != ErrorCode::ET_INVALID && !err.safe()) {
+ *is_unsafe = true;
+ }
+ }
+ }
+}
+
+// A Trap() handler that returns an "errno" value. The value is encoded
+// in the "aux" parameter.
+intptr_t ReturnErrno(const struct arch_seccomp_data&, void *aux) {
+ // TrapFnc functions report error by following the native kernel convention
+ // of returning an exit code in the range of -1..-4096. They do not try to
+ // set errno themselves. The glibc wrapper that triggered the SIGSYS will
+ // ultimately do so for us.
+ int err = reinterpret_cast<intptr_t>(aux) & SECCOMP_RET_DATA;
+ return -err;
+}
+
+// Function that can be passed as a callback function to CodeGen::Traverse().
+// Checks whether the "insn" returns an errno value from a BPF filter. If so,
+// it rewrites the instruction to instead call a Trap() handler that does
+// the same thing. "aux" is ignored.
+void RedirectToUserspace(Instruction *insn, void *aux) {
+ // When inside an UnsafeTrap() callback, we want to allow all system calls.
+ // This means, we must conditionally disable the sandbox -- and that's not
+ // something that kernel-side BPF filters can do, as they cannot inspect
+ // any state other than the syscall arguments.
+ // But if we redirect all error handlers to user-space, then we can easily
+ // make this decision.
+ // The performance penalty for this extra round-trip to user-space is not
+ // actually that bad, as we only ever pay it for denied system calls; and a
+ // typical program has very few of these.
+ Sandbox *sandbox = static_cast<Sandbox *>(aux);
+ if (BPF_CLASS(insn->code) == BPF_RET &&
+ (insn->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
+ insn->k = sandbox->Trap(ReturnErrno,
+ reinterpret_cast<void *>(insn->k & SECCOMP_RET_DATA)).err();
+ }
+}
+
+// Stackable wrapper around an Evaluators handler. Changes ErrorCodes
+// returned by a system call evaluator to match the changes made by
+// RedirectToUserspace(). "aux" should be pointer to wrapped system call
+// evaluator.
+ErrorCode RedirectToUserspaceEvalWrapper(Sandbox *sandbox, int sysnum,
+ void *aux) {
+ // We need to replicate the behavior of RedirectToUserspace(), so that our
+ // Verifier can still work correctly.
+ Sandbox::Evaluators *evaluators =
+ reinterpret_cast<Sandbox::Evaluators *>(aux);
+ const std::pair<Sandbox::EvaluateSyscall, void *>& evaluator =
+ *evaluators->begin();
+
+ ErrorCode err = evaluator.first(sandbox, sysnum, evaluator.second);
+ if ((err.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
+ return sandbox->Trap(ReturnErrno,
+ reinterpret_cast<void *>(err.err() & SECCOMP_RET_DATA));
+ }
+ return err;
+}
+
+intptr_t BpfFailure(const struct arch_seccomp_data&, void *aux) {
+ SANDBOX_DIE(static_cast<char *>(aux));
+}
+
+} // namespace
+
+// The kernel gives us a sandbox, we turn it into a playground :-)
+// This is version 2 of the playground; version 1 was built on top of
+// pre-BPF seccomp mode.
+namespace playground2 {
+
+Sandbox::Sandbox()
+ : quiet_(false),
+ proc_fd_(-1),
+ evaluators_(new Evaluators),
+ conds_(new Conds) {
+}
+
+Sandbox::~Sandbox() {
+ // It is generally unsafe to call any memory allocator operations or to even
+ // call arbitrary destructors after having installed a new policy. We just
+ // have no way to tell whether this policy would allow the system calls that
+ // the constructors can trigger.
+ // So, we normally destroy all of our complex state prior to starting the
+ // sandbox. But this won't happen, if the Sandbox object was created and
+ // never actually used to set up a sandbox. So, just in case, we are
+ // destroying any remaining state.
+ // The "if ()" statements are technically superfluous. But let's be explicit
+ // that we really don't want to run any code, when we already destroyed
+ // objects before setting up the sandbox.
+ if (evaluators_) {
+ delete evaluators_;
+ }
+ if (conds_) {
+ delete conds_;
+ }
+}
+
+bool Sandbox::IsValidSyscallNumber(int sysnum) {
+ return SyscallIterator::IsValid(sysnum);
+}
+
+
bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(),
- EvaluateSyscall syscall_evaluator,
- void *aux,
- int proc_fd) {
+ Sandbox::EvaluateSyscall syscall_evaluator,
+ void *aux) {
// Block all signals before forking a child process. This prevents an
// attacker from manipulating our test by sending us an unexpected signal.
sigset_t old_mask, new_mask;
@@ -168,14 +312,8 @@ bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(),
#endif
}
- evaluators_.clear();
SetSandboxPolicy(syscall_evaluator, aux);
- set_proc_fd(proc_fd);
-
- // By passing "quiet=true" to "startSandboxInternal()" we suppress
- // messages for expected and benign failures (e.g. if the current
- // kernel lacks support for BPF filters).
- StartSandboxInternal(true);
+ StartSandbox();
// Run our code in the sandbox.
code_in_sandbox();
@@ -220,11 +358,10 @@ bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(),
return rc;
}
-bool Sandbox::KernelSupportSeccompBPF(int proc_fd) {
+bool Sandbox::KernelSupportSeccompBPF() {
return
- RunFunctionInPolicy(ProbeProcess, Sandbox::ProbeEvaluator, 0, proc_fd) &&
- RunFunctionInPolicy(TryVsyscallProcess, Sandbox::AllowAllEvaluator, 0,
- proc_fd);
+ RunFunctionInPolicy(ProbeProcess, ProbeEvaluator, 0) &&
+ RunFunctionInPolicy(TryVsyscallProcess, AllowAllEvaluator, 0);
}
Sandbox::SandboxStatus Sandbox::SupportsSeccompSandbox(int proc_fd) {
@@ -259,7 +396,16 @@ Sandbox::SandboxStatus Sandbox::SupportsSeccompSandbox(int proc_fd) {
// we otherwise don't believe to have a good cached value, we have to
// perform a thorough check now.
if (status_ == STATUS_UNKNOWN) {
- status_ = KernelSupportSeccompBPF(proc_fd)
+ // We create our own private copy of a "Sandbox" object. This ensures that
+ // the object does not have any policies configured, that might interfere
+ // with the tests done by "KernelSupportSeccompBPF()".
+ Sandbox sandbox;
+
+ // By setting "quiet_ = true" we suppress messages for expected and benign
+ // failures (e.g. if the current kernel lacks support for BPF filters).
+ sandbox.quiet_ = true;
+ sandbox.set_proc_fd(proc_fd);
+ status_ = sandbox.KernelSupportSeccompBPF()
? STATUS_AVAILABLE : STATUS_UNSUPPORTED;
// As we are performing our tests from a child process, the run-time
@@ -277,13 +423,13 @@ void Sandbox::set_proc_fd(int proc_fd) {
proc_fd_ = proc_fd;
}
-void Sandbox::StartSandboxInternal(bool quiet) {
+void Sandbox::StartSandbox() {
if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) {
SANDBOX_DIE("Trying to start sandbox, even though it is known to be "
"unavailable");
- } else if (status_ == STATUS_ENABLED) {
- SANDBOX_DIE("Cannot start sandbox recursively. Use multiple calls to "
- "setSandboxPolicy() to stack policies instead");
+ } else if (!evaluators_ || !conds_) {
+ SANDBOX_DIE("Cannot repeatedly start sandbox. Create a separate Sandbox "
+ "object instead.");
}
if (proc_fd_ < 0) {
proc_fd_ = open("/proc", O_RDONLY|O_DIRECTORY);
@@ -307,44 +453,17 @@ void Sandbox::StartSandboxInternal(bool quiet) {
}
// Install the filters.
- InstallFilter(quiet);
+ InstallFilter();
// We are now inside the sandbox.
status_ = STATUS_ENABLED;
}
-bool Sandbox::IsSingleThreaded(int proc_fd) {
- if (proc_fd < 0) {
- // Cannot determine whether program is single-threaded. Hope for
- // the best...
- return true;
- }
-
- struct stat sb;
- int task = -1;
- if ((task = openat(proc_fd, "self/task", O_RDONLY|O_DIRECTORY)) < 0 ||
- fstat(task, &sb) != 0 ||
- sb.st_nlink != 3 ||
- HANDLE_EINTR(close(task))) {
- if (task >= 0) {
- if (HANDLE_EINTR(close(task))) { }
- }
- return false;
- }
- return true;
-}
-
-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 syscall_evaluator,
void *aux) {
for (SyscallIterator iter(true); !iter.Done(); ) {
uint32_t sysnum = iter.Next();
- if (!IsDenied(syscall_evaluator(sysnum, aux))) {
+ if (!IsDenied(syscall_evaluator(this, sysnum, aux))) {
SANDBOX_DIE("Policies should deny system calls that are outside the "
"expected range (typically MIN_SYSCALL..MAX_SYSCALL)");
}
@@ -352,60 +471,15 @@ void Sandbox::PolicySanityChecks(EvaluateSyscall syscall_evaluator,
return;
}
-void Sandbox::CheckForUnsafeErrorCodes(Instruction *insn, void *aux) {
- bool *is_unsafe = static_cast<bool *>(aux);
- if (!*is_unsafe) {
- if (BPF_CLASS(insn->code) == BPF_RET &&
- insn->k > SECCOMP_RET_TRAP &&
- insn->k - SECCOMP_RET_TRAP <= SECCOMP_RET_DATA) {
- const ErrorCode& err =
- Trap::ErrorCodeFromTrapId(insn->k & SECCOMP_RET_DATA);
- if (err.error_type_ != ErrorCode::ET_INVALID && !err.safe_) {
- *is_unsafe = true;
- }
- }
- }
-}
-
-void Sandbox::RedirectToUserspace(Instruction *insn, void *) {
- // When inside an UnsafeTrap() callback, we want to allow all system calls.
- // This means, we must conditionally disable the sandbox -- and that's not
- // something that kernel-side BPF filters can do, as they cannot inspect
- // any state other than the syscall arguments.
- // But if we redirect all error handlers to user-space, then we can easily
- // make this decision.
- // The performance penalty for this extra round-trip to user-space is not
- // actually that bad, as we only ever pay it for denied system calls; and a
- // typical program has very few of these.
- if (BPF_CLASS(insn->code) == BPF_RET &&
- (insn->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
- insn->k = Trap(ReturnErrno,
- reinterpret_cast<void *>(insn->k & SECCOMP_RET_DATA)).err();
- }
-}
-
-ErrorCode Sandbox::RedirectToUserspaceEvalWrapper(int sysnum, void *aux) {
- // We need to replicate the behavior of RedirectToUserspace(), so that our
- // Verifier can still work correctly.
- Evaluators *evaluators = reinterpret_cast<Evaluators *>(aux);
- const std::pair<EvaluateSyscall, void *>& evaluator = *evaluators->begin();
- ErrorCode err = evaluator.first(sysnum, evaluator.second);
- if ((err.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
- return Trap(ReturnErrno,
- reinterpret_cast<void *>(err.err() & SECCOMP_RET_DATA));
- }
- return err;
-}
-
void Sandbox::SetSandboxPolicy(EvaluateSyscall syscall_evaluator, void *aux) {
- if (status_ == STATUS_ENABLED) {
+ if (!evaluators_ || !conds_) {
SANDBOX_DIE("Cannot change policy after sandbox has started");
}
PolicySanityChecks(syscall_evaluator, aux);
- evaluators_.push_back(std::make_pair(syscall_evaluator, aux));
+ evaluators_->push_back(std::make_pair(syscall_evaluator, aux));
}
-void Sandbox::InstallFilter(bool quiet) {
+void Sandbox::InstallFilter() {
// We want to be very careful in not imposing any requirements on the
// policies that are set with SetSandboxPolicy(). This means, as soon as
// the sandbox is active, we shouldn't be relying on libraries that could
@@ -426,15 +500,17 @@ void Sandbox::InstallFilter(bool quiet) {
delete program;
// Release memory that is no longer needed
- evaluators_.clear();
- conds_.clear();
+ delete evaluators_;
+ delete conds_;
+ evaluators_ = NULL;
+ conds_ = NULL;
// 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");
+ 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");
+ SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to turn on BPF filters");
}
}
@@ -447,13 +523,13 @@ Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) {
#endif
// Verify that the user pushed a policy.
- if (evaluators_.empty()) {
+ if (evaluators_->empty()) {
SANDBOX_DIE("Failed to configure system call filters");
}
// We can't handle stacked evaluators, yet. We'll get there eventually
// though. Hang tight.
- if (evaluators_.size() != 1) {
+ if (evaluators_->size() != 1) {
SANDBOX_DIE("Not implemented");
}
@@ -509,18 +585,18 @@ Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) {
"architecture");
}
- EvaluateSyscall evaluateSyscall = evaluators_.begin()->first;
- void *aux = evaluators_.begin()->second;
- if (!evaluateSyscall(__NR_rt_sigprocmask, aux).
+ EvaluateSyscall evaluateSyscall = evaluators_->begin()->first;
+ void *aux = evaluators_->begin()->second;
+ if (!evaluateSyscall(this, __NR_rt_sigprocmask, aux).
Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) ||
- !evaluateSyscall(__NR_rt_sigreturn, aux).
+ !evaluateSyscall(this, __NR_rt_sigreturn, aux).
Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
#if defined(__NR_sigprocmask)
- || !evaluateSyscall(__NR_sigprocmask, aux).
+ || !evaluateSyscall(this, __NR_sigprocmask, aux).
Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
#endif
#if defined(__NR_sigreturn)
- || !evaluateSyscall(__NR_sigreturn, aux).
+ || !evaluateSyscall(this, __NR_sigreturn, aux).
Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
#endif
) {
@@ -536,7 +612,7 @@ Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) {
// than enabling dangerous code.
SANDBOX_DIE("We'd rather die than enable unsafe traps");
}
- gen->Traverse(jumptable, RedirectToUserspace, NULL);
+ gen->Traverse(jumptable, RedirectToUserspace, this);
// Allow system calls, if they originate from our magic return address
// (which we can query by calling SandboxSyscall(-1)).
@@ -615,12 +691,13 @@ void Sandbox::VerifyProgram(const Program& program, bool has_unsafe_traps) {
// the verifier would also report a mismatch in return codes.
Evaluators redirected_evaluators;
redirected_evaluators.push_back(
- std::make_pair(RedirectToUserspaceEvalWrapper, &evaluators_));
+ std::make_pair(RedirectToUserspaceEvalWrapper, evaluators_));
const char *err = NULL;
if (!Verifier::VerifyBPF(
+ this,
program,
- has_unsafe_traps ? redirected_evaluators : evaluators_,
+ has_unsafe_traps ? redirected_evaluators : *evaluators_,
&err)) {
CodeGen::PrintProgram(program);
SANDBOX_DIE(err);
@@ -633,14 +710,15 @@ void Sandbox::FindRanges(Ranges *ranges) {
// deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL,
// and then verifying that the rest of the number range (both positive and
// negative) all return the same ErrorCode.
- EvaluateSyscall evaluate_syscall = evaluators_.begin()->first;
- void *aux = evaluators_.begin()->second;
+ EvaluateSyscall evaluate_syscall = evaluators_->begin()->first;
+ void *aux = evaluators_->begin()->second;
uint32_t old_sysnum = 0;
- ErrorCode old_err = evaluate_syscall(old_sysnum, aux);
- ErrorCode invalid_err = evaluate_syscall(MIN_SYSCALL - 1, aux);
+ ErrorCode old_err = evaluate_syscall(this, old_sysnum, aux);
+ ErrorCode invalid_err = evaluate_syscall(this, MIN_SYSCALL - 1,
+ aux);
for (SyscallIterator iter(false); !iter.Done(); ) {
uint32_t sysnum = iter.Next();
- ErrorCode err = evaluate_syscall(static_cast<int>(sysnum), aux);
+ ErrorCode err = evaluate_syscall(this, static_cast<int>(sysnum), aux);
if (!iter.IsValid(sysnum) && !invalid_err.Equals(err)) {
// A proper sandbox policy should always treat system calls outside of
// the range MIN_SYSCALL..MAX_SYSCALL (i.e. anything that returns
@@ -890,25 +968,12 @@ intptr_t Sandbox::ForwardSyscall(const struct arch_seccomp_data& args) {
static_cast<intptr_t>(args.args[5]));
}
-intptr_t Sandbox::ReturnErrno(const struct arch_seccomp_data&, void *aux) {
- // TrapFnc functions report error by following the native kernel convention
- // of returning an exit code in the range of -1..-4096. They do not try to
- // set errno themselves. The glibc wrapper that triggered the SIGSYS will
- // ultimately do so for us.
- int err = reinterpret_cast<intptr_t>(aux) & SECCOMP_RET_DATA;
- return -err;
-}
-
ErrorCode Sandbox::Cond(int argno, ErrorCode::ArgType width,
ErrorCode::Operation op, uint64_t value,
const ErrorCode& passed, const ErrorCode& failed) {
return ErrorCode(argno, width, op, value,
- &*conds_.insert(passed).first,
- &*conds_.insert(failed).first);
-}
-
-intptr_t Sandbox::BpfFailure(const struct arch_seccomp_data&, void *aux) {
- SANDBOX_DIE(static_cast<char *>(aux));
+ &*conds_->insert(passed).first,
+ &*conds_->insert(failed).first);
}
ErrorCode Sandbox::Kill(const char *msg) {
@@ -916,8 +981,5 @@ ErrorCode Sandbox::Kill(const char *msg) {
}
Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN;
-int Sandbox::proc_fd_ = -1;
-Sandbox::Evaluators Sandbox::evaluators_;
-Sandbox::Conds Sandbox::conds_;
} // namespace
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
index 3e21527..3d26991 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.h
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -5,33 +5,9 @@
#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
-#include <endian.h>
-#include <errno.h>
-#include <fcntl.h>
-// #include <linux/audit.h>
-#include <linux/filter.h>
-// #include <linux/seccomp.h>
-#include <linux/unistd.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <netinet/udp.h>
-#include <sched.h>
-#include <signal.h>
#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/ipc.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <sys/stat.h>
#include <sys/types.h>
-#include <sys/uio.h>
#include <sys/wait.h>
-#include <time.h>
-#include <unistd.h>
#include <algorithm>
#include <limits>
@@ -40,212 +16,11 @@
#include <utility>
#include <vector>
-#if !defined(SECCOMP_BPF_STANDALONE)
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "base/posix/eintr_wrapper.h"
-#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.
-
-// For audit.h
-#ifndef EM_ARM
-#define EM_ARM 40
-#endif
-#ifndef EM_386
-#define EM_386 3
-#endif
-#ifndef EM_X86_64
-#define EM_X86_64 62
-#endif
-
-#ifndef __AUDIT_ARCH_64BIT
-#define __AUDIT_ARCH_64BIT 0x80000000
-#endif
-#ifndef __AUDIT_ARCH_LE
-#define __AUDIT_ARCH_LE 0x40000000
-#endif
-#ifndef AUDIT_ARCH_ARM
-#define AUDIT_ARCH_ARM (EM_ARM|__AUDIT_ARCH_LE)
-#endif
-#ifndef AUDIT_ARCH_I386
-#define AUDIT_ARCH_I386 (EM_386|__AUDIT_ARCH_LE)
-#endif
-#ifndef AUDIT_ARCH_X86_64
-#define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
-#endif
-
-// For prctl.h
-#ifndef PR_SET_SECCOMP
-#define PR_SET_SECCOMP 22
-#define PR_GET_SECCOMP 21
-#endif
-#ifndef PR_SET_NO_NEW_PRIVS
-#define PR_SET_NO_NEW_PRIVS 38
-#define PR_GET_NO_NEW_PRIVS 39
-#endif
-#ifndef IPC_64
-#define IPC_64 0x0100
-#endif
-
-#ifndef BPF_MOD
-#define BPF_MOD 0x90
-#endif
-#ifndef BPF_XOR
-#define BPF_XOR 0xA0
-#endif
-
-// In order to build will older tool chains, we currently have to avoid
-// including <linux/seccomp.h>. Until that can be fixed (if ever). Rely on
-// our own definitions of the seccomp kernel ABI.
-#ifndef SECCOMP_MODE_FILTER
-#define SECCOMP_MODE_DISABLED 0
-#define SECCOMP_MODE_STRICT 1
-#define SECCOMP_MODE_FILTER 2 // User user-supplied filter
-#endif
-
-#ifndef SECCOMP_RET_KILL
-// Return values supported for BPF filter programs. Please note that the
-// "illegal" SECCOMP_RET_INVALID is not supported by the kernel, should only
-// ever be used internally, and would result in the kernel killing our process.
-#define SECCOMP_RET_KILL 0x00000000U // Kill the task immediately
-#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
-#define SECCOMP_RET_TRAP 0x00030000U // Disallow and force a SIGSYS
-#define SECCOMP_RET_ERRNO 0x00050000U // Returns an errno
-#define SECCOMP_RET_TRACE 0x7ff00000U // Pass to a tracer or disallow
-#define SECCOMP_RET_ALLOW 0x7fff0000U // Allow
-#define SECCOMP_RET_ACTION 0xffff0000U // Masks for the return value
-#define SECCOMP_RET_DATA 0x0000ffffU // sections
-#else
-#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
-#endif
-
-#ifndef SYS_SECCOMP
-#define SYS_SECCOMP 1
-#endif
-
-// Impose some reasonable maximum BPF program size. Realistically, the
-// kernel probably has much lower limits. But by limiting to less than
-// 30 bits, we can ease requirements on some of our data types.
-#define SECCOMP_MAX_PROGRAM_SIZE (1<<30)
-
-#if defined(__i386__)
-#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)])
-#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_EAX)
-#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_EAX)
-#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_EIP)
-#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_EBX)
-#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_ECX)
-#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_EDX)
-#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_ESI)
-#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_EDI)
-#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_EBP)
-#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
-#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
-#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
- instruction_pointer) + 4)
-#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
- instruction_pointer) + 0)
-#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
- 8*(nr) + 4)
-#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
- 8*(nr) + 0)
-
-#elif defined(__x86_64__)
-#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)])
-#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_RAX)
-#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_RAX)
-#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_RIP)
-#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_RDI)
-#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_RSI)
-#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_RDX)
-#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_R10)
-#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_R8)
-#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_R9)
-#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
-#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
-#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
- instruction_pointer) + 4)
-#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
- instruction_pointer) + 0)
-#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
- 8*(nr) + 4)
-#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
- 8*(nr) + 0)
-
-#elif defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))
-// ARM EABI includes "ARM private" system calls starting at |__ARM_NR_BASE|,
-// 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_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)
-
-#define SECCOMP_ARCH AUDIT_ARCH_ARM
-
-// ARM sigcontext_t is different from i386/x86_64.
-// See </arch/arm/include/asm/sigcontext.h> in the Linux kernel.
-#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.arm_##_reg)
-// ARM EABI syscall convention.
-#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, r0)
-#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, r7)
-#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, pc)
-#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, r0)
-#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, r1)
-#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, r2)
-#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, r3)
-#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, r4)
-#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, r5)
-#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
-#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
-#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
- instruction_pointer) + 4)
-#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
- instruction_pointer) + 0)
-#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
- 8*(nr) + 4)
-#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
- 8*(nr) + 0)
-
-#else
-#error Unsupported target platform
-
-#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&)
-
-template <bool>
-struct CompileAssert {
-};
-#define COMPILE_ASSERT(expr, msg) \
- typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
-#endif
-
#include "sandbox/linux/seccomp-bpf/die.h"
#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/port.h"
+
namespace playground2 {
@@ -281,13 +56,26 @@ class Sandbox {
// each time a call is made through an EvaluateSyscall function pointer.
// One common use case would be to pass the "aux" pointer as an argument
// to Trap() functions.
- typedef ErrorCode (*EvaluateSyscall)(int sysnum, void *aux);
+ typedef ErrorCode (*EvaluateSyscall)(Sandbox *sb, int sysnum, void *aux);
typedef std::vector<std::pair<EvaluateSyscall, void *> >Evaluators;
// A vector of BPF instructions that need to be installed as a filter
// program in the kernel.
typedef std::vector<struct sock_filter> Program;
+ // Constructors and destructors.
+ // NOTE: Setting a policy and starting the sandbox is a one-way operation.
+ // The kernel does not provide any option for unloading a loaded
+ // sandbox. Strictly speaking, that means we should disallow calling
+ // the destructor, if StartSandbox() has ever been called. In practice,
+ // this makes it needlessly complicated to operate on "Sandbox"
+ // objects. So, we instead opted to allow object destruction. But it
+ // should be noted that during its lifetime, the object probably made
+ // irreversible state changes to the runtime environment. These changes
+ // stay in effect even after the destructor has been run.
+ Sandbox();
+ ~Sandbox();
+
// 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.
@@ -304,8 +92,8 @@ class Sandbox {
// directory is not accessible when "startSandbox()" gets called, the caller
// can provide an already opened file descriptor by calling "set_proc_fd()".
// The sandbox becomes the new owner of this file descriptor and will
- // eventually close it when "startSandbox()" executes.
- static void set_proc_fd(int proc_fd);
+ // eventually close it when "StartSandbox()" executes.
+ void set_proc_fd(int proc_fd);
// The system call evaluator function is called with the system
// call number. It can decide to allow the system call unconditionally
@@ -318,15 +106,15 @@ class Sandbox {
// handler. In this case, of course, the data that is pointed to must remain
// valid for the entire time that Trap() handlers can be called; typically,
// this would be the lifetime of the program.
- static void SetSandboxPolicy(EvaluateSyscall syscallEvaluator, void *aux);
+ void SetSandboxPolicy(EvaluateSyscall syscallEvaluator, void *aux);
// 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.
// The "aux" field can carry a pointer to arbitrary data. See EvaluateSyscall
- // for a description of how to pass data from setSandboxPolicy() to a Trap()
+ // for a description of how to pass data from SetSandboxPolicy() to a Trap()
// handler.
- static ErrorCode Trap(Trap::TrapFnc fnc, const void *aux);
+ ErrorCode Trap(Trap::TrapFnc fnc, const void *aux);
// Calls a user-space trap handler and disables all sandboxing for system
// calls made from this trap handler.
@@ -338,7 +126,7 @@ class Sandbox {
// very useful to diagnose code that is incompatible with the sandbox.
// If even a single system call returns "UnsafeTrap", the security of
// entire sandbox should be considered compromised.
- static ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void *aux);
+ ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void *aux);
// From within an UnsafeTrap() it is often useful to be able to execute
// the system call that triggered the trap. The ForwardSyscall() method
@@ -360,18 +148,26 @@ class Sandbox {
// If it is outside this range, the sandbox treats the system call just
// the same as any other ABI violation (i.e. it aborts with an error
// message).
- static ErrorCode Cond(int argno, ErrorCode::ArgType is_32bit,
- ErrorCode::Operation op,
- uint64_t value, const ErrorCode& passed,
- const ErrorCode& failed);
+ ErrorCode Cond(int argno, ErrorCode::ArgType is_32bit,
+ ErrorCode::Operation op,
+ uint64_t value, const ErrorCode& passed,
+ const ErrorCode& failed);
// Kill the program and print an error message.
- static ErrorCode Kill(const char *msg);
+ 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); }
+ // It is possible to stack multiple sandboxes by creating separate "Sandbox"
+ // objects and calling "StartSandbox()" on each of them. Please note, that
+ // this requires special care, though, as newly stacked sandboxes can never
+ // relax restrictions imposed by earlier sandboxes. Furthermore, installing
+ // a new policy requires making system calls, that might already be
+ // disallowed.
+ // Finally, stacking does add more kernel overhead than having a single
+ // combined policy. So, it should only be used if there are no alternatives.
+ void StartSandbox();
// Assembles a BPF filter program from the current policy. After calling this
// function, you must not call any other sandboxing function.
@@ -381,14 +177,17 @@ class Sandbox {
// through the verifier, iff the program was built in debug mode.
// But by setting "force_verification", the caller can request that the
// verifier is run unconditionally. This is useful for unittests.
- static Program *AssembleFilter(bool force_verification);
+ Program *AssembleFilter(bool force_verification);
+
+ // Returns the fatal ErrorCode that is used to indicate that somebody
+ // attempted to pass a 64bit value in a 32bit system call argument.
+ // This method is primarily needed for testing purposes.
+ ErrorCode Unexpected64bitArgument();
private:
friend class CodeGen;
friend class SandboxUnittestHelper;
friend class ErrorCode;
- friend class Util;
- friend class Verifier;
struct Range {
Range(uint32_t f, uint32_t t, const ErrorCode& e)
@@ -404,91 +203,65 @@ class Sandbox {
typedef std::set<ErrorCode, struct ErrorCode::LessThan> Conds;
// Get a file descriptor pointing to "/proc", if currently available.
- static int proc_fd() { return proc_fd_; }
-
- static ErrorCode ProbeEvaluator(int sysnum, void *) __attribute__((const));
- static void ProbeProcess(void);
- static ErrorCode AllowAllEvaluator(int sysnum, void *aux);
- static void TryVsyscallProcess(void);
- static bool KernelSupportSeccompBPF(int proc_fd);
- static bool RunFunctionInPolicy(void (*function)(),
- EvaluateSyscall syscall_evaluator,
- void *aux,
- 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 syscall_evaluator,
- void *aux);
-
- // Function that can be passed as a callback function to CodeGen::Traverse().
- // Checks whether the "insn" returns an UnsafeTrap() ErrorCode. If so, it
- // sets the "bool" variable pointed to by "aux".
- static void CheckForUnsafeErrorCodes(Instruction *insn, void *aux);
-
- // Function that can be passed as a callback function to CodeGen::Traverse().
- // Checks whether the "insn" returns an errno value from a BPF filter. If so,
- // it rewrites the instruction to instead call a Trap() handler that does
- // the same thing. "aux" is ignored.
- static void RedirectToUserspace(Instruction *insn, void *aux);
-
- // Stackable wrapper around an Evaluators handler. Changes ErrorCodes
- // returned by a system call evaluator to match the changes made by
- // RedirectToUserspace(). "aux" should be pointer to wrapped system call
- // evaluator.
- static ErrorCode RedirectToUserspaceEvalWrapper(int sysnum, void *aux);
+ int proc_fd() { return proc_fd_; }
+
+ // Creates a subprocess and runs "code_in_sandbox" inside of the specified
+ // policy. The caller has to make sure that "this" has not yet been
+ // initialized with any other policies.
+ bool RunFunctionInPolicy(void (*code_in_sandbox)(),
+ EvaluateSyscall syscall_evaluator, void *aux);
+
+ // Performs a couple of sanity checks to verify that the kernel supports the
+ // features that we need for successful sandboxing.
+ // The caller has to make sure that "this" has not yet been initialized with
+ // any other policies.
+ bool KernelSupportSeccompBPF();
+
+ // Verify that the current policy passes some basic sanity checks.
+ void PolicySanityChecks(EvaluateSyscall syscall_evaluator, void *aux);
// Assembles and installs a filter based on the policy that has previously
// been configured with SetSandboxPolicy().
- static void InstallFilter(bool quiet);
+ void InstallFilter();
// Verify the correctness of a compiled program by comparing it against the
// current policy. This function should only ever be called by unit tests and
// by the sandbox internals. It should not be used by production code.
- static void VerifyProgram(const Program& program, bool has_unsafe_traps);
+ void VerifyProgram(const Program& program, bool has_unsafe_traps);
// Finds all the ranges of system calls that need to be handled. Ranges are
// sorted in ascending order of system call numbers. There are no gaps in the
// ranges. System calls with identical ErrorCodes are coalesced into a single
// range.
- static void FindRanges(Ranges *ranges);
+ void FindRanges(Ranges *ranges);
// Returns a BPF program snippet that implements a jump table for the
// given range of system call numbers. This function runs recursively.
- static Instruction *AssembleJumpTable(CodeGen *gen,
- Ranges::const_iterator start,
- Ranges::const_iterator stop);
+ Instruction *AssembleJumpTable(CodeGen *gen,
+ Ranges::const_iterator start,
+ Ranges::const_iterator stop);
// Returns a BPF program snippet that makes the BPF filter program exit
// with the given ErrorCode "err". N.B. the ErrorCode may very well be a
// conditional expression; if so, this function will recursively call
// CondExpression() and possibly RetExpression() to build a complex set of
// instructions.
- static Instruction *RetExpression(CodeGen *gen, const ErrorCode& err);
+ Instruction *RetExpression(CodeGen *gen, const ErrorCode& err);
// Returns a BPF program that evaluates the conditional expression in
// "cond" and returns the appropriate value from the BPF filter program.
// This function recursively calls RetExpression(); it should only ever be
// called from RetExpression().
- static Instruction *CondExpression(CodeGen *gen, const ErrorCode& cond);
-
- // Returns the fatal ErrorCode that is used to indicate that somebody
- // attempted to pass a 64bit value in a 32bit system call argument.
- static ErrorCode Unexpected64bitArgument();
-
- // A Trap() handler that returns an "errno" value. The value is encoded
- // in the "aux" parameter.
- static intptr_t ReturnErrno(const struct arch_seccomp_data&, void *aux);
-
- static intptr_t BpfFailure(const struct arch_seccomp_data& data, void *aux);
+ Instruction *CondExpression(CodeGen *gen, const ErrorCode& cond);
static SandboxStatus status_;
- static int proc_fd_;
- static Evaluators evaluators_;
- static Conds conds_;
- DISALLOW_IMPLICIT_CONSTRUCTORS(Sandbox);
+ bool quiet_;
+ int proc_fd_;
+ Evaluators *evaluators_;
+ Conds *conds_;
+
+ DISALLOW_COPY_AND_ASSIGN(Sandbox);
};
} // namespace
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
index 8ddc695..b5a62c2 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
@@ -80,11 +80,11 @@ intptr_t FakeGetPid(const struct arch_seccomp_data& args, void *aux) {
return (*pid_ptr)++;
}
-ErrorCode VerboseAPITestingPolicy(int sysno, void *aux) {
+ErrorCode VerboseAPITestingPolicy(Sandbox *sandbox, int sysno, void *aux) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
} else if (sysno == __NR_getpid) {
- return Sandbox::Trap(FakeGetPid, aux);
+ return sandbox->Trap(FakeGetPid, aux);
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
@@ -94,8 +94,9 @@ SANDBOX_TEST(SandboxBpf, VerboseAPITesting) {
if (Sandbox::SupportsSeccompSandbox(-1) ==
playground2::Sandbox::STATUS_AVAILABLE) {
pid_t test_var = 0;
- playground2::Sandbox::SetSandboxPolicy(VerboseAPITestingPolicy, &test_var);
- playground2::Sandbox::StartSandbox();
+ Sandbox sandbox;
+ sandbox.SetSandboxPolicy(VerboseAPITestingPolicy, &test_var);
+ sandbox.StartSandbox();
BPF_ASSERT(test_var == 0);
BPF_ASSERT(syscall(__NR_getpid) == 0);
@@ -111,7 +112,7 @@ SANDBOX_TEST(SandboxBpf, VerboseAPITesting) {
// A simple blacklist test
-ErrorCode BlacklistNanosleepPolicy(int sysno, void *) {
+ErrorCode BlacklistNanosleepPolicy(Sandbox *, int sysno, void *) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
@@ -135,7 +136,7 @@ BPF_TEST(SandboxBpf, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) {
// Now do a simple whitelist test
-ErrorCode WhitelistGetpidPolicy(int sysno, void *) {
+ErrorCode WhitelistGetpidPolicy(Sandbox *, int sysno, void *) {
switch (sysno) {
case __NR_getpid:
case __NR_exit_group:
@@ -165,7 +166,8 @@ intptr_t EnomemHandler(const struct arch_seccomp_data& args, void *aux) {
return -ENOMEM;
}
-ErrorCode BlacklistNanosleepPolicySigsys(int sysno, void *aux) {
+ErrorCode BlacklistNanosleepPolicySigsys(Sandbox *sandbox, int sysno,
+ void *aux) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
@@ -173,7 +175,7 @@ ErrorCode BlacklistNanosleepPolicySigsys(int sysno, void *aux) {
switch (sysno) {
case __NR_nanosleep:
- return Sandbox::Trap(EnomemHandler, aux);
+ return sandbox->Trap(EnomemHandler, aux);
default:
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
@@ -198,7 +200,7 @@ BPF_TEST(SandboxBpf, BasicBlacklistWithSigsys,
// A simple test that verifies we can return arbitrary errno values.
-ErrorCode ErrnoTestPolicy(int sysno, void *) {
+ErrorCode ErrnoTestPolicy(Sandbox *, int sysno, void *) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
@@ -247,6 +249,60 @@ BPF_TEST(SandboxBpf, ErrnoTest, ErrnoTestPolicy) {
BPF_ASSERT(errno == ErrorCode::ERR_MAX_ERRNO);
}
+// Testing the stacking of two sandboxes
+
+ErrorCode StackingPolicyPartOne(Sandbox *sandbox, int sysno, void *) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ return ErrorCode(ENOSYS);
+ }
+
+ switch (sysno) {
+ case __NR_getppid:
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ ErrorCode(EPERM));
+ default:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+ErrorCode StackingPolicyPartTwo(Sandbox *sandbox, int sysno, void *) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ return ErrorCode(ENOSYS);
+ }
+
+ switch (sysno) {
+ case __NR_getppid:
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+ ErrorCode(EINVAL),
+ ErrorCode(ErrorCode::ERR_ALLOWED));
+ default:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+BPF_TEST(SandboxBpf, StackingPolicy, StackingPolicyPartOne) {
+ errno = 0;
+ BPF_ASSERT(syscall(__NR_getppid, 0) > 0);
+ BPF_ASSERT(errno == 0);
+
+ BPF_ASSERT(syscall(__NR_getppid, 1) == -1);
+ BPF_ASSERT(errno == EPERM);
+
+ // Stack a second sandbox with its own policy. Verify that we can further
+ // restrict filters, but we cannot relax existing filters.
+ Sandbox sandbox;
+ sandbox.SetSandboxPolicy(StackingPolicyPartTwo, NULL);
+ sandbox.StartSandbox();
+
+ errno = 0;
+ BPF_ASSERT(syscall(__NR_getppid, 0) == -1);
+ BPF_ASSERT(errno == EINVAL);
+
+ BPF_ASSERT(syscall(__NR_getppid, 1) == -1);
+ BPF_ASSERT(errno == EPERM);
+}
+
// A more complex, but synthetic policy. This tests the correctness of the BPF
// program by iterating through all syscalls and checking for an errno that
// depends on the syscall number. Unlike the Verifier, this exercises the BPF
@@ -262,7 +318,7 @@ int SysnoToRandomErrno(int sysno) {
return ((sysno & ~3) >> 2) % 29 + 1;
}
-ErrorCode SyntheticPolicy(int sysno, void *) {
+ErrorCode SyntheticPolicy(Sandbox *, int sysno, void *) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
@@ -320,7 +376,7 @@ int ArmPrivateSysnoToErrno(int sysno) {
}
}
-ErrorCode ArmPrivatePolicy(int sysno, void *) {
+ErrorCode ArmPrivatePolicy(Sandbox *, int sysno, void *) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy.
return ErrorCode(ENOSYS);
@@ -360,7 +416,7 @@ intptr_t CountSyscalls(const struct arch_seccomp_data& args, void *aux) {
return Sandbox::ForwardSyscall(args);
}
-ErrorCode GreyListedPolicy(int sysno, void *aux) {
+ErrorCode GreyListedPolicy(Sandbox *sandbox, int sysno, void *aux) {
// The use of UnsafeTrap() causes us to print a warning message. This is
// generally desirable, but it results in the unittest failing, as it doesn't
// expect any messages on "stderr". So, temporarily disable messages. The
@@ -386,7 +442,7 @@ ErrorCode GreyListedPolicy(int sysno, void *aux) {
return ErrorCode(EPERM);
} else if (Sandbox::IsValidSyscallNumber(sysno)) {
// Allow (and count) all other system calls.
- return Sandbox::UnsafeTrap(CountSyscalls, aux);
+ return sandbox->UnsafeTrap(CountSyscalls, aux);
} else {
return ErrorCode(ENOSYS);
}
@@ -430,13 +486,13 @@ intptr_t PrctlHandler(const struct arch_seccomp_data& args, void *) {
}
}
-ErrorCode PrctlPolicy(int sysno, void *aux) {
+ErrorCode PrctlPolicy(Sandbox *sandbox, int sysno, void *aux) {
setenv(kSandboxDebuggingEnv, "t", 0);
Die::SuppressInfoMessages(true);
if (sysno == __NR_prctl) {
// Handle prctl() inside an UnsafeTrap()
- return Sandbox::UnsafeTrap(PrctlHandler, NULL);
+ return sandbox->UnsafeTrap(PrctlHandler, NULL);
} else if (Sandbox::IsValidSyscallNumber(sysno)) {
// Allow all other system calls.
return ErrorCode(ErrorCode::ERR_ALLOWED);
@@ -472,7 +528,7 @@ intptr_t AllowRedirectedSyscall(const struct arch_seccomp_data& args, void *) {
return Sandbox::ForwardSyscall(args);
}
-ErrorCode RedirectAllSyscallsPolicy(int sysno, void *aux) {
+ErrorCode RedirectAllSyscallsPolicy(Sandbox *sandbox, int sysno, void *aux) {
setenv(kSandboxDebuggingEnv, "t", 0);
Die::SuppressInfoMessages(true);
@@ -489,7 +545,7 @@ ErrorCode RedirectAllSyscallsPolicy(int sysno, void *aux) {
) {
return ErrorCode(ErrorCode::ERR_ALLOWED);
} else if (Sandbox::IsValidSyscallNumber(sysno)) {
- return Sandbox::UnsafeTrap(AllowRedirectedSyscall, aux);
+ return sandbox->UnsafeTrap(AllowRedirectedSyscall, aux);
} else {
return ErrorCode(ENOSYS);
}
@@ -620,7 +676,7 @@ intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args,
}
}
-ErrorCode DenyOpenPolicy(int sysno, void *aux) {
+ErrorCode DenyOpenPolicy(Sandbox *sandbox, int sysno, void *aux) {
InitializedOpenBroker* iob = static_cast<InitializedOpenBroker*>(aux);
if (!Sandbox::IsValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
@@ -631,7 +687,7 @@ ErrorCode DenyOpenPolicy(int sysno, void *aux) {
case __NR_openat:
// We get a InitializedOpenBroker class, but our trap handler wants
// the BrokerProcess object.
- return ErrorCode(Sandbox::Trap(BrokerOpenTrapHandler,
+ return ErrorCode(sandbox->Trap(BrokerOpenTrapHandler,
iob->broker_process()));
default:
return ErrorCode(ErrorCode::ERR_ALLOWED);
@@ -675,7 +731,7 @@ BPF_TEST(SandboxBpf, UseOpenBroker, DenyOpenPolicy,
// Simple test demonstrating how to use Sandbox::Cond()
-ErrorCode SimpleCondTestPolicy(int sysno, void *) {
+ErrorCode SimpleCondTestPolicy(Sandbox *sandbox, int sysno, void *) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
@@ -688,17 +744,17 @@ ErrorCode SimpleCondTestPolicy(int sysno, void *) {
case __NR_open:
// Allow opening files for reading, but don't allow writing.
COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_be_all_zero_bits);
- return Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
O_ACCMODE /* 0x3 */,
ErrorCode(EROFS),
ErrorCode(ErrorCode::ERR_ALLOWED));
case __NR_prctl:
// Allow prctl(PR_SET_DUMPABLE) and prctl(PR_GET_DUMPABLE), but
// disallow everything else.
- return Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
PR_SET_DUMPABLE,
ErrorCode(ErrorCode::ERR_ALLOWED),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
PR_GET_DUMPABLE,
ErrorCode(ErrorCode::ERR_ALLOWED),
ErrorCode(ENOMEM)));
@@ -760,7 +816,7 @@ class EqualityStressTest {
}
}
- ErrorCode Policy(int sysno) {
+ ErrorCode Policy(Sandbox *sandbox, int sysno) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
@@ -773,7 +829,7 @@ class EqualityStressTest {
} else {
// ToErrorCode() turns an ArgValue object into an ErrorCode that is
// suitable for use by a sandbox policy.
- return ToErrorCode(arg_values_[sysno]);
+ return ToErrorCode(sandbox, arg_values_[sysno]);
}
}
@@ -923,7 +979,7 @@ class EqualityStressTest {
}
}
- ErrorCode ToErrorCode(ArgValue *arg_value) {
+ ErrorCode ToErrorCode(Sandbox *sandbox, ArgValue *arg_value) {
// Compute the ErrorCode that should be returned, if none of our
// tests succeed (i.e. the system call parameter doesn't match any
// of the values in arg_value->tests[].k_value).
@@ -936,7 +992,7 @@ class EqualityStressTest {
// If this wasn't a leaf node yet, recursively descend into the rest
// of the tree. This will end up adding a few more Sandbox::Cond()
// tests to our ErrorCode.
- err = ToErrorCode(arg_value->arg_value);
+ err = ToErrorCode(sandbox, arg_value->arg_value);
}
// Now, iterate over all the test cases that we want to compare against.
@@ -948,12 +1004,12 @@ class EqualityStressTest {
if (arg_value->tests[n].err) {
matched = ErrorCode(arg_value->tests[n].err);
} else {
- matched = ToErrorCode(arg_value->tests[n].arg_value);
+ matched = ToErrorCode(sandbox, arg_value->tests[n].arg_value);
}
// For now, all of our tests are limited to 32bit.
// We have separate tests that check the behavior of 32bit vs. 64bit
// conditional expressions.
- err = Sandbox::Cond(arg_value->argno, ErrorCode::TP_32BIT,
+ err = sandbox->Cond(arg_value->argno, ErrorCode::TP_32BIT,
ErrorCode::OP_EQUAL, arg_value->tests[n].k_value,
matched, err);
}
@@ -1021,8 +1077,8 @@ class EqualityStressTest {
static const int kMaxArgs = 6;
};
-ErrorCode EqualityStressTestPolicy(int sysno, void *aux) {
- return reinterpret_cast<EqualityStressTest *>(aux)->Policy(sysno);
+ErrorCode EqualityStressTestPolicy(Sandbox *sandbox, int sysno, void *aux) {
+ return reinterpret_cast<EqualityStressTest *>(aux)->Policy(sandbox, sysno);
}
BPF_TEST(SandboxBpf, EqualityTests, EqualityStressTestPolicy,
@@ -1030,13 +1086,13 @@ BPF_TEST(SandboxBpf, EqualityTests, EqualityStressTestPolicy,
BPF_AUX.VerifyFilter();
}
-ErrorCode EqualityArgumentWidthPolicy(int sysno, void *) {
+ErrorCode EqualityArgumentWidthPolicy(Sandbox *sandbox, int sysno, void *) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
} else if (sysno == __NR_uname) {
- return Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
- Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
0x55555555, ErrorCode(1), ErrorCode(2)),
// The BPF compiler and the BPF interpreter in the kernel are
// (mostly) agnostic of the host platform's word size. The compiler
@@ -1046,7 +1102,7 @@ ErrorCode EqualityArgumentWidthPolicy(int sysno, void *) {
// in a 64bit quantity on a 32bit platform. The upper 32bits should
// always be zero. So, this test should always evaluate as false on
// 32bit systems.
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_EQUAL,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_EQUAL,
0x55555555AAAAAAAAULL, ErrorCode(1), ErrorCode(2)));
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
@@ -1080,12 +1136,13 @@ BPF_DEATH_TEST(SandboxBpf, EqualityArgumentUnallowed64bit,
}
#endif
-ErrorCode EqualityWithNegativeArgumentsPolicy(int sysno, void *) {
+ErrorCode EqualityWithNegativeArgumentsPolicy(Sandbox *sandbox, int sysno,
+ void *) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
} else if (sysno == __NR_uname) {
- return Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
0xFFFFFFFF, ErrorCode(1), ErrorCode(2));
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
@@ -1110,7 +1167,7 @@ BPF_DEATH_TEST(SandboxBpf, EqualityWithNegative64bitArguments,
}
#endif
-ErrorCode AllBitTestPolicy(int sysno, void *) {
+ErrorCode AllBitTestPolicy(Sandbox *sandbox, int sysno, void *) {
// Test the OP_HAS_ALL_BITS conditional test operator with a couple of
// different bitmasks. We try to find bitmasks that could conceivably
// touch corner cases.
@@ -1121,64 +1178,61 @@ ErrorCode AllBitTestPolicy(int sysno, void *) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
} else if (sysno == __NR_uname) {
- return Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
- Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
0x0,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 1,
- Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 1,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
0x1,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 2,
- Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 2,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
0x3,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 3,
- Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 3,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
0x80000000,
ErrorCode(1), ErrorCode(0)),
-
- // All the following tests don't really make much sense on 32bit
- // systems. They will always evaluate as false.
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 4,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 4,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
0x0,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 5,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 5,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
0x1,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 6,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 6,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
0x3,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 7,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 7,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
0x80000000,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 8,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 8,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
0x100000000ULL,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 9,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 9,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
0x300000000ULL,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 10,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 10,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
0x100000001ULL,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Kill("Invalid test case number"))))))))))));
+ sandbox->Kill("Invalid test case number"))))))))))));
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
@@ -1311,7 +1365,7 @@ BPF_TEST(SandboxBpf, AllBitTests, AllBitTestPolicy) {
BITMASK_TEST(10, -1L, ALLBITS64,0x100000001, EXPT64_SUCCESS);
}
-ErrorCode AnyBitTestPolicy(int sysno, void *) {
+ErrorCode AnyBitTestPolicy(Sandbox *sandbox, int sysno, void *) {
// Test the OP_HAS_ANY_BITS conditional test operator with a couple of
// different bitmasks. We try to find bitmasks that could conceivably
// touch corner cases.
@@ -1322,64 +1376,64 @@ ErrorCode AnyBitTestPolicy(int sysno, void *) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
} else if (sysno == __NR_uname) {
- return Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
- Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
0x0,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 1,
- Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 1,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
0x1,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 2,
- Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 2,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
0x3,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 3,
- Sandbox::Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 3,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
0x80000000,
ErrorCode(1), ErrorCode(0)),
// All the following tests don't really make much sense on 32bit
// systems. They will always evaluate as false.
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 4,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 4,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
0x0,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 5,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 5,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
0x1,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 6,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 6,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
0x3,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 7,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 7,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
0x80000000,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 8,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 8,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
0x100000000ULL,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 9,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 9,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
0x300000000ULL,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 10,
- Sandbox::Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 10,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
0x100000001ULL,
ErrorCode(1), ErrorCode(0)),
- Sandbox::Kill("Invalid test case number"))))))))))));
+ sandbox->Kill("Invalid test case number"))))))))))));
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
@@ -1512,7 +1566,7 @@ intptr_t PthreadTrapHandler(const struct arch_seccomp_data& args, void *aux) {
return -EPERM;
}
-ErrorCode PthreadPolicyEquality(int sysno, void *aux) {
+ErrorCode PthreadPolicyEquality(Sandbox *sandbox, int sysno, void *aux) {
// This policy allows creating threads with pthread_create(). But it
// doesn't allow any other uses of clone(). Most notably, it does not
// allow callers to implement fork() or vfork() by passing suitable flags
@@ -1528,23 +1582,22 @@ ErrorCode PthreadPolicyEquality(int sysno, void *aux) {
// The following policy is very strict. It only allows the exact masks
// that we have seen in known implementations. It is probably somewhat
// stricter than what we would want to do.
- return Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|
CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|
CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,
ErrorCode(ErrorCode::ERR_ALLOWED),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|
CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED,
ErrorCode(ErrorCode::ERR_ALLOWED),
- Sandbox::Trap(PthreadTrapHandler, aux)));
-
+ sandbox->Trap(PthreadTrapHandler, aux)));
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
-ErrorCode PthreadPolicyBitMask(int sysno, void *aux) {
+ErrorCode PthreadPolicyBitMask(Sandbox *sandbox, int sysno, void *aux) {
// This policy allows creating threads with pthread_create(). But it
// doesn't allow any other uses of clone(). Most notably, it does not
// allow callers to implement fork() or vfork() by passing suitable flags
@@ -1562,26 +1615,26 @@ ErrorCode PthreadPolicyBitMask(int sysno, void *aux) {
// err on the side of rather safe than sorry.
// Very noticeably though, we disallow fork() (which is often just a
// wrapper around clone()).
- return Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
~uint32(CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|
CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|
CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID|
CLONE_DETACHED),
- Sandbox::Trap(PthreadTrapHandler,
+ sandbox->Trap(PthreadTrapHandler,
"Unexpected CLONE_XXX flag found"),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|
CLONE_THREAD|CLONE_SYSVSEM,
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,
ErrorCode(ErrorCode::ERR_ALLOWED),
- Sandbox::Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,
- Sandbox::Trap(PthreadTrapHandler,
+ sandbox->Trap(PthreadTrapHandler,
"Must set either all or none of the TLS"
" and futex bits in call to clone()"),
ErrorCode(ErrorCode::ERR_ALLOWED))),
- Sandbox::Trap(PthreadTrapHandler,
+ sandbox->Trap(PthreadTrapHandler,
"Missing mandatory CLONE_XXX flags "
"when creating new thread")));
} else {
diff --git a/sandbox/linux/seccomp-bpf/syscall.cc b/sandbox/linux/seccomp-bpf/syscall.cc
index b5721e6..8b09a68 100644
--- a/sandbox/linux/seccomp-bpf/syscall.cc
+++ b/sandbox/linux/seccomp-bpf/syscall.cc
@@ -5,7 +5,7 @@
#include <asm/unistd.h>
#include <errno.h>
-#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/port.h"
#include "sandbox/linux/seccomp-bpf/syscall.h"
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator.cc b/sandbox/linux/seccomp-bpf/syscall_iterator.cc
index 59a4e863..4ea979a 100644
--- a/sandbox/linux/seccomp-bpf/syscall_iterator.cc
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator.cc
@@ -2,7 +2,8 @@
// 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/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/port.h"
#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
namespace playground2 {
diff --git a/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
index 11fa315..136deb6 100644
--- a/sandbox/linux/seccomp-bpf/syscall_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
@@ -78,12 +78,12 @@ intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void *aux) {
return -ENOMEM;
}
-ErrorCode CopyAllArgsOnUnamePolicy(int sysno, void *aux) {
+ErrorCode CopyAllArgsOnUnamePolicy(Sandbox *sandbox, int sysno, void *aux) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
}
if (sysno == __NR_uname) {
- return Sandbox::Trap(CopySyscallArgsToAux, aux);
+ return sandbox->Trap(CopySyscallArgsToAux, aux);
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
diff --git a/sandbox/linux/seccomp-bpf/trap.cc b/sandbox/linux/seccomp-bpf/trap.cc
index 64875ad..85d7d36 100644
--- a/sandbox/linux/seccomp-bpf/trap.cc
+++ b/sandbox/linux/seccomp-bpf/trap.cc
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <errno.h>
#include <signal.h>
+#include <string.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
diff --git a/sandbox/linux/seccomp-bpf/trap.h b/sandbox/linux/seccomp-bpf/trap.h
index 40e7109..db29757 100644
--- a/sandbox/linux/seccomp-bpf/trap.h
+++ b/sandbox/linux/seccomp-bpf/trap.h
@@ -5,9 +5,15 @@
#ifndef SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
#define SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
+#include <signal.h>
+#include <stdint.h>
+
#include <map>
#include <vector>
+#include "sandbox/linux/seccomp-bpf/port.h"
+
+
namespace playground2 {
class ErrorCode;
diff --git a/sandbox/linux/seccomp-bpf/util.cc b/sandbox/linux/seccomp-bpf/util.cc
deleted file mode 100644
index 77a92d9..0000000
--- a/sandbox/linux/seccomp-bpf/util.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-// 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 <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
-#include "sandbox/linux/seccomp-bpf/util.h"
-
-namespace playground2 {
-
-bool Util::SendFds(int transport, const void *buf, size_t len, ...) {
- int count = 0;
- va_list ap;
- va_start(ap, len);
- while (va_arg(ap, int) >= 0) {
- ++count;
- }
- va_end(ap);
- if (!count) {
- return false;
- }
- char cmsg_buf[CMSG_SPACE(count*sizeof(int))];
- memset(cmsg_buf, 0, sizeof(cmsg_buf));
- struct iovec iov[2] = { { 0 } };
- struct msghdr msg = { 0 };
- int dummy = 0;
- iov[0].iov_base = &dummy;
- iov[0].iov_len = sizeof(dummy);
- if (buf && len > 0) {
- iov[1].iov_base = const_cast<void *>(buf);
- iov[1].iov_len = len;
- }
- msg.msg_iov = iov;
- msg.msg_iovlen = (buf && len > 0) ? 2 : 1;
- msg.msg_control = cmsg_buf;
- msg.msg_controllen = CMSG_LEN(count*sizeof(int));
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(count*sizeof(int));
- va_start(ap, len);
- for (int i = 0, fd; (fd = va_arg(ap, int)) >= 0; ++i) {
- (reinterpret_cast<int *>(CMSG_DATA(cmsg)))[i] = fd;
- }
- return sendmsg(transport, &msg, 0) ==
- static_cast<ssize_t>(sizeof(dummy) + ((buf && len > 0) ? len : 0));
-}
-
-bool Util::GetFds(int transport, void *buf, size_t *len, ...) {
- int count = 0;
- va_list ap;
- va_start(ap, len);
- for (int *fd; (fd = va_arg(ap, int *)) != NULL; ++count) {
- *fd = -1;
- }
- va_end(ap);
- if (!count) {
- return false;
- }
- char cmsg_buf[CMSG_SPACE(count*sizeof(int))];
- memset(cmsg_buf, 0, sizeof(cmsg_buf));
- struct iovec iov[2] = { { 0 } };
- struct msghdr msg = { 0 };
- int err;
- iov[0].iov_base = &err;
- iov[0].iov_len = sizeof(int);
- if (buf && len && *len > 0) {
- iov[1].iov_base = buf;
- iov[1].iov_len = *len;
- }
- msg.msg_iov = iov;
- msg.msg_iovlen = (buf && len && *len > 0) ? 2 : 1;
- msg.msg_control = cmsg_buf;
- msg.msg_controllen = CMSG_LEN(count*sizeof(int));
- ssize_t bytes = recvmsg(transport, &msg, 0);
- if (len) {
- *len = bytes > static_cast<int>(sizeof(int)) ? bytes - sizeof(int) : 0;
- }
- if (bytes != static_cast<ssize_t>(sizeof(int) + iov[1].iov_len)) {
- if (bytes >= 0) {
- errno = 0;
- }
- return false;
- }
- if (err) {
- // "err" is the first four bytes of the payload. If these are non-zero,
- // the sender on the other side of the socketpair sent us an errno value.
- // We don't expect to get any file handles in this case.
- errno = err;
- return false;
- }
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
- if ((msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) ||
- !cmsg ||
- cmsg->cmsg_level != SOL_SOCKET ||
- cmsg->cmsg_type != SCM_RIGHTS ||
- cmsg->cmsg_len != CMSG_LEN(count*sizeof(int))) {
- errno = EBADF;
- return false;
- }
- va_start(ap, len);
- for (int *fd, i = 0; (fd = va_arg(ap, int *)) != NULL; ++i) {
- *fd = (reinterpret_cast<int *>(CMSG_DATA(cmsg)))[i];
- }
- va_end(ap);
- return true;
-}
-
-void Util::CloseAllBut(int fd, ...) {
- int proc_fd;
- int fdir;
- 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\"");
- }
- int dev_null = open("/dev/null", O_RDWR);
- DIR *dir = fdopendir(fdir);
- struct dirent de, *res;
- while (!readdir_r(dir, &de, &res) && res) {
- if (res->d_name[0] < '0') {
- continue;
- }
- int i = atoi(res->d_name);
- if (i >= 0 && i != dirfd(dir) && i != dev_null) {
- va_list ap;
- va_start(ap, fd);
- for (int f = fd;; f = va_arg(ap, int)) {
- if (f < 0) {
- if (i <= 2) {
- // Never ever close 0..2. If we cannot redirect to /dev/null,
- // then we are better off leaving the standard descriptors open.
- if (dev_null >= 0) {
- if (HANDLE_EINTR(dup2(dev_null, i))) {
- SANDBOX_DIE("Cannot dup2()");
- }
- }
- } else {
- if (HANDLE_EINTR(close(i))) { }
- }
- break;
- } else if (i == f) {
- break;
- }
- }
- va_end(ap);
- }
- }
- closedir(dir);
- if (dev_null >= 0) {
- if (HANDLE_EINTR(close(dev_null))) { }
- }
- return;
-}
-
-} // namespace
diff --git a/sandbox/linux/seccomp-bpf/util.h b/sandbox/linux/seccomp-bpf/util.h
deleted file mode 100644
index 2b3f22a..0000000
--- a/sandbox/linux/seccomp-bpf/util.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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_UTIL_H__
-#define SANDBOX_LINUX_SECCOMP_BPF_UTIL_H__
-
-namespace playground2 {
-
-class Util {
- public:
- static bool SendFds(int transport, const void *buf, size_t len, ...);
- static bool GetFds(int transport, void *buf, size_t *len, ...);
- static void CloseAllBut(int fd, ...);
-};
-
-} // namespace
-
-#endif // SANDBOX_LINUX_SECCOMP_BPF_UTIL_H__
diff --git a/sandbox/linux/seccomp-bpf/verifier.cc b/sandbox/linux/seccomp-bpf/verifier.cc
index 06143b9..60c0eab 100644
--- a/sandbox/linux/seccomp-bpf/verifier.cc
+++ b/sandbox/linux/seccomp-bpf/verifier.cc
@@ -2,86 +2,76 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <string.h>
+
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
#include "sandbox/linux/seccomp-bpf/verifier.h"
-namespace playground2 {
+namespace {
-bool Verifier::VerifyBPF(const std::vector<struct sock_filter>& program,
- const Sandbox::Evaluators& evaluators,
- const char **err) {
- *err = NULL;
- if (evaluators.size() != 1) {
- *err = "Not implemented";
- return false;
- }
- Sandbox::EvaluateSyscall evaluate_syscall = evaluators.begin()->first;
- void *aux = evaluators.begin()->second;
- 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".
- // On Intel systems, this can fail in a surprising way, as a cleared bit 30
- // 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 = { static_cast<int>(sysnum),
- static_cast<uint32_t>(SECCOMP_ARCH) };
-#if defined(__i386__) || defined(__x86_64__)
-#if defined(__x86_64__) && defined(__ILP32__)
- if (!(sysnum & 0x40000000u)) {
- continue;
- }
-#else
- if (sysnum & 0x40000000u) {
- continue;
- }
-#endif
-#endif
- ErrorCode code = evaluate_syscall(sysnum, aux);
- if (!VerifyErrorCode(program, &data, code, code, err)) {
- return false;
- }
+using playground2::ErrorCode;
+using playground2::Sandbox;
+using playground2::Verifier;
+using playground2::arch_seccomp_data;
+
+struct State {
+ State(const std::vector<struct sock_filter>& p,
+ const struct arch_seccomp_data& d) :
+ program(p),
+ data(d),
+ ip(0),
+ accumulator(0),
+ acc_is_valid(false) {
}
- return true;
-}
+ const std::vector<struct sock_filter>& program;
+ const struct arch_seccomp_data& data;
+ unsigned int ip;
+ uint32_t accumulator;
+ bool acc_is_valid;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(State);
+};
-uint32_t Verifier::EvaluateErrorCode(const ErrorCode& code,
- const struct arch_seccomp_data& data) {
- if (code.error_type_ == ErrorCode::ET_SIMPLE ||
- code.error_type_ == ErrorCode::ET_TRAP) {
- return code.err_;
- } else if (code.error_type_ == ErrorCode::ET_COND) {
- if (code.width_ == ErrorCode::TP_32BIT &&
- (data.args[code.argno_] >> 32) &&
- (data.args[code.argno_]&0xFFFFFFFF80000000ull)!=0xFFFFFFFF80000000ull){
- return Sandbox::Unexpected64bitArgument().err();
+uint32_t EvaluateErrorCode(Sandbox *sandbox, const ErrorCode& code,
+ const struct arch_seccomp_data& data) {
+ if (code.error_type() == ErrorCode::ET_SIMPLE ||
+ code.error_type() == ErrorCode::ET_TRAP) {
+ return code.err();
+ } else if (code.error_type() == ErrorCode::ET_COND) {
+ if (code.width() == ErrorCode::TP_32BIT &&
+ (data.args[code.argno()] >> 32) &&
+ (data.args[code.argno()] & 0xFFFFFFFF80000000ull) !=
+ 0xFFFFFFFF80000000ull) {
+ return sandbox->Unexpected64bitArgument().err();
}
- switch (code.op_) {
+ switch (code.op()) {
case ErrorCode::OP_EQUAL:
- return EvaluateErrorCode((code.width_ == ErrorCode::TP_32BIT
- ? uint32_t(data.args[code.argno_])
- : data.args[code.argno_]) == code.value_
- ? *code.passed_
- : *code.failed_,
+ return EvaluateErrorCode(sandbox,
+ (code.width() == ErrorCode::TP_32BIT
+ ? uint32_t(data.args[code.argno()])
+ : data.args[code.argno()]) == code.value()
+ ? *code.passed()
+ : *code.failed(),
data);
case ErrorCode::OP_HAS_ALL_BITS:
- return EvaluateErrorCode(((code.width_ == ErrorCode::TP_32BIT
- ? uint32_t(data.args[code.argno_])
- : data.args[code.argno_]) & code.value_)
- == code.value_
- ? *code.passed_
- : *code.failed_,
+ return EvaluateErrorCode(sandbox,
+ ((code.width() == ErrorCode::TP_32BIT
+ ? uint32_t(data.args[code.argno()])
+ : data.args[code.argno()]) & code.value())
+ == code.value()
+ ? *code.passed()
+ : *code.failed(),
data);
case ErrorCode::OP_HAS_ANY_BITS:
- return EvaluateErrorCode((code.width_ == ErrorCode::TP_32BIT
- ? uint32_t(data.args[code.argno_])
- : data.args[code.argno_]) & code.value_
- ? *code.passed_
- : *code.failed_,
+ return EvaluateErrorCode(sandbox,
+ (code.width() == ErrorCode::TP_32BIT
+ ? uint32_t(data.args[code.argno()])
+ : data.args[code.argno()]) & code.value()
+ ? *code.passed()
+ : *code.failed(),
data);
default:
return SECCOMP_RET_INVALID;
@@ -91,19 +81,20 @@ uint32_t Verifier::EvaluateErrorCode(const ErrorCode& code,
}
}
-bool Verifier::VerifyErrorCode(const std::vector<struct sock_filter>& program,
- struct arch_seccomp_data *data,
- const ErrorCode& root_code,
- const ErrorCode& code,
- const char **err) {
- if (code.error_type_ == ErrorCode::ET_SIMPLE ||
- code.error_type_ == ErrorCode::ET_TRAP) {
- uint32_t computed_ret = EvaluateBPF(program, *data, err);
+bool VerifyErrorCode(Sandbox *sandbox,
+ const std::vector<struct sock_filter>& program,
+ struct arch_seccomp_data *data,
+ const ErrorCode& root_code,
+ const ErrorCode& code,
+ const char **err) {
+ if (code.error_type() == ErrorCode::ET_SIMPLE ||
+ code.error_type() == ErrorCode::ET_TRAP) {
+ uint32_t computed_ret = Verifier::EvaluateBPF(program, *data, err);
if (*err) {
return false;
- } else if (computed_ret != EvaluateErrorCode(root_code, *data)) {
+ } else if (computed_ret != EvaluateErrorCode(sandbox, root_code, *data)) {
// For efficiency's sake, we'd much rather compare "computed_ret"
- // against "code.err_". This works most of the time, but it doesn't
+ // against "code.err()". This works most of the time, but it doesn't
// always work for nested conditional expressions. The test values
// that we generate on the fly to probe expressions can trigger
// code flow decisions in multiple nodes of the decision tree, and the
@@ -112,32 +103,34 @@ bool Verifier::VerifyErrorCode(const std::vector<struct sock_filter>& program,
*err = "Exit code from BPF program doesn't match";
return false;
}
- } else if (code.error_type_ == ErrorCode::ET_COND) {
- if (code.argno_ < 0 || code.argno_ >= 6) {
+ } else if (code.error_type() == ErrorCode::ET_COND) {
+ if (code.argno() < 0 || code.argno() >= 6) {
*err = "Invalid argument number in error code";
return false;
}
- switch (code.op_) {
+ switch (code.op()) {
case ErrorCode::OP_EQUAL:
// Verify that we can check a 32bit value (or the LSB of a 64bit value)
// for equality.
- data->args[code.argno_] = code.value_;
- if (!VerifyErrorCode(program, data, root_code, *code.passed_, err)) {
+ data->args[code.argno()] = code.value();
+ if (!VerifyErrorCode(sandbox, program, data, root_code,
+ *code.passed(), err)) {
return false;
}
// Change the value to no longer match and verify that this is detected
// as an inequality.
- data->args[code.argno_] = code.value_ ^ 0x55AA55AA;
- if (!VerifyErrorCode(program, data, root_code, *code.failed_, err)) {
+ data->args[code.argno()] = code.value() ^ 0x55AA55AA;
+ if (!VerifyErrorCode(sandbox, program, data, root_code,
+ *code.failed(), err)) {
return false;
}
// BPF programs can only ever operate on 32bit values. So, we have
// generated additional BPF instructions that inspect the MSB. Verify
// that they behave as intended.
- if (code.width_ == ErrorCode::TP_32BIT) {
- if (code.value_ >> 32) {
+ if (code.width() == ErrorCode::TP_32BIT) {
+ if (code.value() >> 32) {
SANDBOX_DIE("Invalid comparison of a 32bit system call argument "
"against a 64bit constant; this test is always false.");
}
@@ -145,9 +138,10 @@ bool Verifier::VerifyErrorCode(const std::vector<struct sock_filter>& program,
// If the system call argument was intended to be a 32bit parameter,
// verify that it is a fatal error if a 64bit value is ever passed
// here.
- data->args[code.argno_] = 0x100000000ull;
- if (!VerifyErrorCode(program, data, root_code,
- Sandbox::Unexpected64bitArgument(), err)) {
+ data->args[code.argno()] = 0x100000000ull;
+ if (!VerifyErrorCode(sandbox, program, data, root_code,
+ sandbox->Unexpected64bitArgument(),
+ err)) {
return false;
}
} else {
@@ -157,8 +151,9 @@ bool Verifier::VerifyErrorCode(const std::vector<struct sock_filter>& program,
// We only need to verify the behavior of the inequality test. We
// know that the equality test already passed, as unlike the kernel
// the Verifier does operate on 64bit quantities.
- data->args[code.argno_] = code.value_ ^ 0x55AA55AA00000000ull;
- if (!VerifyErrorCode(program, data, root_code, *code.failed_, err)) {
+ data->args[code.argno()] = code.value() ^ 0x55AA55AA00000000ull;
+ if (!VerifyErrorCode(sandbox, program, data, root_code,
+ *code.failed(), err)) {
return false;
}
}
@@ -172,37 +167,37 @@ bool Verifier::VerifyErrorCode(const std::vector<struct sock_filter>& program,
// ones.
{
// Testing "any" bits against a zero mask is always false. So, there
- // are some cases, where we expect tests to take the "failed_" branch
- // even though this is a test that normally should take "passed_".
+ // are some cases, where we expect tests to take the "failed()" branch
+ // even though this is a test that normally should take "passed()".
const ErrorCode& passed =
- (!code.value_ && code.op_ == ErrorCode::OP_HAS_ANY_BITS) ||
+ (!code.value() && code.op() == ErrorCode::OP_HAS_ANY_BITS) ||
// On a 32bit system, it is impossible to pass a 64bit value as a
// system call argument. So, some additional tests always evaluate
// as false.
- ((code.value_ & ~uint64_t(uintptr_t(-1))) &&
- code.op_ == ErrorCode::OP_HAS_ALL_BITS) ||
- (code.value_ && !(code.value_ & uintptr_t(-1)) &&
- code.op_ == ErrorCode::OP_HAS_ANY_BITS)
+ ((code.value() & ~uint64_t(uintptr_t(-1))) &&
+ code.op() == ErrorCode::OP_HAS_ALL_BITS) ||
+ (code.value() && !(code.value() & uintptr_t(-1)) &&
+ code.op() == ErrorCode::OP_HAS_ANY_BITS)
- ? *code.failed_ : *code.passed_;
+ ? *code.failed() : *code.passed();
// Similary, testing for "all" bits in a zero mask is always true. So,
// some cases pass despite them normally failing.
const ErrorCode& failed =
- !code.value_ && code.op_ == ErrorCode::OP_HAS_ALL_BITS
- ? *code.passed_ : *code.failed_;
+ !code.value() && code.op() == ErrorCode::OP_HAS_ALL_BITS
+ ? *code.passed() : *code.failed();
- data->args[code.argno_] = code.value_ & uintptr_t(-1);
- if (!VerifyErrorCode(program, data, root_code, passed, err)) {
+ data->args[code.argno()] = code.value() & uintptr_t(-1);
+ if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) {
return false;
}
- data->args[code.argno_] = uintptr_t(-1);
- if (!VerifyErrorCode(program, data, root_code, passed, err)) {
+ data->args[code.argno()] = uintptr_t(-1);
+ if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) {
return false;
}
- data->args[code.argno_] = 0;
- if (!VerifyErrorCode(program, data, root_code, failed, err)) {
+ data->args[code.argno()] = 0;
+ if (!VerifyErrorCode(sandbox, program, data, root_code, failed, err)) {
return false;
}
}
@@ -218,55 +213,7 @@ bool Verifier::VerifyErrorCode(const std::vector<struct sock_filter>& program,
return true;
}
-uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
- const struct arch_seccomp_data& data,
- const char **err) {
- *err = NULL;
- if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) {
- *err = "Invalid program length";
- return 0;
- }
- for (State state(program, data); !*err; ++state.ip) {
- if (state.ip >= program.size()) {
- *err = "Invalid instruction pointer in BPF program";
- break;
- }
- const struct sock_filter& insn = program[state.ip];
- switch (BPF_CLASS(insn.code)) {
- case BPF_LD:
- Ld(&state, insn, err);
- break;
- case BPF_JMP:
- Jmp(&state, insn, err);
- break;
- case BPF_RET: {
- uint32_t r = Ret(&state, insn, err);
- switch (r & SECCOMP_RET_ACTION) {
- case SECCOMP_RET_TRAP:
- case SECCOMP_RET_ERRNO:
- case SECCOMP_RET_ALLOW:
- break;
- case SECCOMP_RET_KILL: // We don't ever generate this
- case SECCOMP_RET_TRACE: // We don't ever generate this
- case SECCOMP_RET_INVALID: // Should never show up in BPF program
- default:
- *err = "Unexpected return code found in BPF program";
- return 0;
- }
- return r; }
- case BPF_ALU:
- Alu(&state, insn, err);
- break;
- default:
- *err = "Unexpected instruction in BPF program";
- break;
- }
- }
- return 0;
-}
-
-void Verifier::Ld(State *state, const struct sock_filter& insn,
- const char **err) {
+void Ld(State *state, const struct sock_filter& insn, const char **err) {
if (BPF_SIZE(insn.code) != BPF_W ||
BPF_MODE(insn.code) != BPF_ABS) {
*err = "Invalid BPF_LD instruction";
@@ -285,8 +232,7 @@ void Verifier::Ld(State *state, const struct sock_filter& insn,
return;
}
-void Verifier::Jmp(State *state, const struct sock_filter& insn,
- const char **err) {
+void Jmp(State *state, const struct sock_filter& insn, const char **err) {
if (BPF_OP(insn.code) == BPF_JA) {
if (state->ip + insn.k + 1 >= state->program.size() ||
state->ip + insn.k + 1 <= state->ip) {
@@ -337,8 +283,7 @@ void Verifier::Jmp(State *state, const struct sock_filter& insn,
}
}
-uint32_t Verifier::Ret(State *, const struct sock_filter& insn,
- const char **err) {
+uint32_t Ret(State *, const struct sock_filter& insn, const char **err) {
if (BPF_SRC(insn.code) != BPF_K) {
*err = "Invalid BPF_RET instruction";
return 0;
@@ -346,8 +291,7 @@ uint32_t Verifier::Ret(State *, const struct sock_filter& insn,
return insn.k;
}
-void Verifier::Alu(State *state, const struct sock_filter& insn,
- const char **err) {
+void Alu(State *state, const struct sock_filter& insn, const char **err) {
if (BPF_OP(insn.code) == BPF_NEG) {
state->accumulator = -state->accumulator;
return;
@@ -410,5 +354,96 @@ void Verifier::Alu(State *state, const struct sock_filter& insn,
}
}
+} // namespace
+
+namespace playground2 {
+
+bool Verifier::VerifyBPF(Sandbox *sandbox,
+ const std::vector<struct sock_filter>& program,
+ const Sandbox::Evaluators& evaluators,
+ const char **err) {
+ *err = NULL;
+ if (evaluators.size() != 1) {
+ *err = "Not implemented";
+ return false;
+ }
+ Sandbox::EvaluateSyscall evaluate_syscall = evaluators.begin()->first;
+ void *aux = evaluators.begin()->second;
+ 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".
+ // On Intel systems, this can fail in a surprising way, as a cleared bit 30
+ // 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 = { static_cast<int>(sysnum),
+ static_cast<uint32_t>(SECCOMP_ARCH) };
+#if defined(__i386__) || defined(__x86_64__)
+#if defined(__x86_64__) && defined(__ILP32__)
+ if (!(sysnum & 0x40000000u)) {
+ continue;
+ }
+#else
+ if (sysnum & 0x40000000u) {
+ continue;
+ }
+#endif
+#endif
+ ErrorCode code = evaluate_syscall(sandbox, sysnum, aux);
+ if (!VerifyErrorCode(sandbox, program, &data, code, code, err)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
+ const struct arch_seccomp_data& data,
+ const char **err) {
+ *err = NULL;
+ if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) {
+ *err = "Invalid program length";
+ return 0;
+ }
+ for (State state(program, data); !*err; ++state.ip) {
+ if (state.ip >= program.size()) {
+ *err = "Invalid instruction pointer in BPF program";
+ break;
+ }
+ const struct sock_filter& insn = program[state.ip];
+ switch (BPF_CLASS(insn.code)) {
+ case BPF_LD:
+ Ld(&state, insn, err);
+ break;
+ case BPF_JMP:
+ Jmp(&state, insn, err);
+ break;
+ case BPF_RET: {
+ uint32_t r = Ret(&state, insn, err);
+ switch (r & SECCOMP_RET_ACTION) {
+ case SECCOMP_RET_TRAP:
+ case SECCOMP_RET_ERRNO:
+ case SECCOMP_RET_ALLOW:
+ break;
+ case SECCOMP_RET_KILL: // We don't ever generate this
+ case SECCOMP_RET_TRACE: // We don't ever generate this
+ case SECCOMP_RET_INVALID: // Should never show up in BPF program
+ default:
+ *err = "Unexpected return code found in BPF program";
+ return 0;
+ }
+ return r; }
+ case BPF_ALU:
+ Alu(&state, insn, err);
+ break;
+ default:
+ *err = "Unexpected instruction in BPF program";
+ break;
+ }
+ }
+ return 0;
+}
} // namespace
diff --git a/sandbox/linux/seccomp-bpf/verifier.h b/sandbox/linux/seccomp-bpf/verifier.h
index 8fcb9f2..3e99a08 100644
--- a/sandbox/linux/seccomp-bpf/verifier.h
+++ b/sandbox/linux/seccomp-bpf/verifier.h
@@ -10,8 +10,6 @@
#include <utility>
#include <vector>
-#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
-
namespace playground2 {
@@ -24,7 +22,8 @@ class Verifier {
// set by the "evaluators".
// Upon success, "err" is set to NULL. Upon failure, it contains a static
// error message that does not need to be free()'d.
- static bool VerifyBPF(const std::vector<struct sock_filter>& program,
+ static bool VerifyBPF(Sandbox *sandbox,
+ const std::vector<struct sock_filter>& program,
const Sandbox::Evaluators& evaluators,
const char **err);
@@ -41,40 +40,6 @@ class Verifier {
const char **err);
private:
- struct State {
- State(const std::vector<struct sock_filter>& p,
- const struct arch_seccomp_data& d) :
- program(p),
- data(d),
- ip(0),
- accumulator(0),
- acc_is_valid(false) {
- }
- const std::vector<struct sock_filter>& program;
- const struct arch_seccomp_data& data;
- unsigned int ip;
- uint32_t accumulator;
- bool acc_is_valid;
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(State);
- };
-
- static uint32_t EvaluateErrorCode(const ErrorCode& code,
- const struct arch_seccomp_data& data);
- static bool VerifyErrorCode(const std::vector<struct sock_filter>& prg,
- struct arch_seccomp_data *data,
- const ErrorCode& root_code,
- const ErrorCode& code, const char **err);
- static void Ld (State *state, const struct sock_filter& insn,
- const char **err);
- static void Jmp(State *state, const struct sock_filter& insn,
- const char **err);
- static uint32_t Ret(State *state, const struct sock_filter& insn,
- const char **err);
- static void Alu(State *state, const struct sock_filter& insn,
- const char **err);
-
DISALLOW_IMPLICIT_CONSTRUCTORS(Verifier);
};