diff options
author | jln@chromium.org <jln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-23 12:12:44 +0000 |
---|---|---|
committer | jln@chromium.org <jln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-23 12:12:44 +0000 |
commit | e96f4b297b856ab72768309868dc23b83c840d37 (patch) | |
tree | 60bd5843e1b4f4c85dadc3a806ec76e2422303df /sandbox | |
parent | c491b6280b4684a5d73287f02d7d4b868a7e8bda (diff) | |
download | chromium_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.cc | 130 | ||||
-rw-r--r-- | sandbox/linux/tests/unit_tests.cc | 5 |
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(); } |