summaryrefslogtreecommitdiffstats
path: root/sandbox
diff options
context:
space:
mode:
authorjln@chromium.org <jln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-23 12:12:44 +0000
committerjln@chromium.org <jln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-23 12:12:44 +0000
commite96f4b297b856ab72768309868dc23b83c840d37 (patch)
tree60bd5843e1b4f4c85dadc3a806ec76e2422303df /sandbox
parentc491b6280b4684a5d73287f02d7d4b868a7e8bda (diff)
downloadchromium_src-e96f4b297b856ab72768309868dc23b83c840d37.zip
chromium_src-e96f4b297b856ab72768309868dc23b83c840d37.tar.gz
chromium_src-e96f4b297b856ab72768309868dc23b83c840d37.tar.bz2
Seccomp BPF: add a simple whitelist unittest
BUG= TEST= Review URL: https://chromiumcodereview.appspot.com/10663011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@143804 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc130
-rw-r--r--sandbox/linux/tests/unit_tests.cc5
2 files changed, 113 insertions, 22 deletions
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
index 8c7ab22..aa2c114 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
@@ -10,6 +10,8 @@ using namespace playground2;
namespace {
+const int kExpectedReturnValue = 42;
+
TEST(SandboxBpf, CallSupports) {
// We check that we don't crash, but it's ok if the kernel doesn't
// support it.
@@ -21,8 +23,78 @@ TEST(SandboxBpf, CallSupportsTwice) {
Sandbox::supportsSeccompSandbox(-1);
}
-static Sandbox::ErrorCode NanosleepEvaluator(int sysno) {
- if (sysno < (int) MIN_SYSCALL || sysno > (int) MAX_SYSCALL) {
+__attribute__((noreturn)) void DoCrash() {
+ // Cause a #PF. This only works if we assume that we have the default
+ // SIGSEGV handler.
+ *(reinterpret_cast<volatile char*>(NULL)) = '\0';
+ for (;;) {
+ // If we didn't manage to crash there is really nothing we can do reliably
+ // but spin.
+ }
+}
+
+__attribute__((noreturn)) void ExitGroup(int status) {
+ syscall(__NR_exit_group, status);
+ // If exit_group() failed, there is a high likelihood this
+ // happened due to a bug in the sandbox. We therefore cannot
+ // blindly assume that the failure happened because we are
+ // running on an old kernel that supports exit(), but not
+ // exit_group(). We cannot even trust "errno" returning
+ // "ENOSYS". So, calling exit() as the fallback for
+ // exit_group() would at this point almost certainly be a
+ // bug. Instead, try some more aggressive methods to make
+ // the program stop.
+ DoCrash();
+}
+
+// Helper function to start a sandbox with a policy specified in
+// evaluator
+void StartSandboxOrDie(Sandbox::EvaluateSyscall evaluator) {
+ int proc_fd = open("/proc", O_RDONLY|O_DIRECTORY);
+ if (proc_fd < 0 || !evaluator) {
+ ExitGroup(1);
+ }
+ if (Sandbox::supportsSeccompSandbox(proc_fd) !=
+ Sandbox::STATUS_AVAILABLE) {
+ ExitGroup(1);
+ }
+ Sandbox::setProcFd(proc_fd);
+ Sandbox::setSandboxPolicy(evaluator, NULL);
+ Sandbox::startSandbox();
+}
+
+void RunInSandbox(Sandbox::EvaluateSyscall evaluator,
+ void (*SandboxedCode)()) {
+ // TODO(jln): Implement IsEqual for ErrorCode
+ // IsEqual(evaluator(__NR_exit_group), Sandbox::SB_ALLOWED) <<
+ // "You need to always allow exit_group() in your test policy";
+ StartSandboxOrDie(evaluator);
+ // TODO(jln): find a way to use the testing framework inside
+ // SandboxedCode() or at the very least to surface errors
+ SandboxedCode();
+ // SandboxedCode() should have exited, this is a failure
+ ExitGroup(1);
+}
+
+// evaluator should always allow ExitGroup
+// SandboxedCode should ExitGroup(kExpectedReturnValue) if and only if
+// things go as expected.
+void TryPolicyInProcess(Sandbox::EvaluateSyscall evaluator,
+ void (*SandboxedCode)()) {
+ // TODO(jln) figure out a way to surface whether we're actually testing
+ // something or not.
+ if (Sandbox::supportsSeccompSandbox(-1) == Sandbox::STATUS_AVAILABLE) {
+ EXPECT_EXIT(RunInSandbox(evaluator, SandboxedCode),
+ ::testing::ExitedWithCode(kExpectedReturnValue),
+ "");
+ }
+}
+
+// A simple blacklist test
+
+Sandbox::ErrorCode BlacklistNanosleepPolicy(int sysno) {
+ if (sysno < static_cast<int>(MIN_SYSCALL) ||
+ sysno > static_cast<int>(MAX_SYSCALL)) {
// FIXME: we should really not have to do that in a trivial policy
return ENOSYS;
}
@@ -34,30 +106,44 @@ static Sandbox::ErrorCode NanosleepEvaluator(int sysno) {
}
}
-void BasicPolicyProcess(void) {
- int proc_fd = open("/proc", O_RDONLY|O_DIRECTORY);
- if (proc_fd < 0)
- exit(-1);
- if(Sandbox::supportsSeccompSandbox(proc_fd) !=
- Sandbox::STATUS_AVAILABLE)
- exit(-1);
- Sandbox::setProcFd(proc_fd);
- Sandbox::setSandboxPolicy(NanosleepEvaluator, NULL);
- Sandbox::startSandbox();
+void NanosleepProcess(void) {
const struct timespec ts = {0, 0};
- if(nanosleep(&ts, NULL) != -1 || errno != EACCES)
- exit(-1);
- exit(0);
+ errno = 0;
+ if(syscall(__NR_nanosleep, &ts, NULL) != -1 || errno != EACCES) {
+ ExitGroup(1);
+ }
+ ExitGroup(kExpectedReturnValue);
}
-TEST(SandboxBpf, CanApplyBasicPolicy) {
- // This test will pass if seccomp-bpf is not supported
- if(Sandbox::supportsSeccompSandbox(-1) ==
- Sandbox::STATUS_AVAILABLE) {
- // TODO: find a way to use the testing testing framework inside
- // BasicPolicyProcess or at the very least to surface errors
- EXPECT_EXIT(BasicPolicyProcess(), ::testing::ExitedWithCode(0), "");
+TEST(SandboxBpf, ApplyBasicBlacklistPolicy) {
+ TryPolicyInProcess(BlacklistNanosleepPolicy, NanosleepProcess);
+}
+
+// Now do a simple whitelist test
+
+Sandbox::ErrorCode WhitelistGetpidPolicy(int sysno) {
+ switch (sysno) {
+ case __NR_getpid:
+ case __NR_exit_group:
+ return Sandbox::SB_ALLOWED;
+ default:
+ return ENOMEM;
}
}
+void GetpidProcess(void) {
+ errno = 0;
+ // getpid() should be allowed
+ if (syscall(__NR_getpid) < 0 || errno)
+ ExitGroup(1);
+ // getpgid() should be denied
+ if (getpgid(0) != -1 || errno != ENOMEM)
+ ExitGroup(1);
+ ExitGroup(kExpectedReturnValue);
+}
+
+TEST(SandboxBpf, ApplyBasicWhitelistPolicy) {
+ TryPolicyInProcess(WhitelistGetpidPolicy, GetpidProcess);
+}
+
} // namespace
diff --git a/sandbox/linux/tests/unit_tests.cc b/sandbox/linux/tests/unit_tests.cc
index 999d4a6..2e24d8c 100644
--- a/sandbox/linux/tests/unit_tests.cc
+++ b/sandbox/linux/tests/unit_tests.cc
@@ -5,5 +5,10 @@
#include "testing/gtest/include/gtest/gtest.h"
int main(int argc, char *argv[]) {
testing::InitGoogleTest(&argc, argv);
+ // Always go through re-execution for death tests.
+ // This makes gtest only marginally slower for us and has the
+ // additional side effect of getting rid of gtest warnings about fork()
+ // safety.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
return RUN_ALL_TESTS();
}