diff options
author | markus@chromium.org <markus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-26 01:39:39 +0000 |
---|---|---|
committer | markus@chromium.org <markus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-26 01:39:39 +0000 |
commit | 083fe1e0bc4ead809af46a75fbb979cc95e7491d (patch) | |
tree | d4a47bc54dc91671fc917968e7ad5ed6203b474b /sandbox | |
parent | 3124fbfa6a99ad601c71bb2edf403ddb5cdf080b (diff) | |
download | chromium_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')
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); }; |