Index: sandbox/linux/tests/unit_tests.cc |
diff --git a/sandbox/linux/tests/unit_tests.cc b/sandbox/linux/tests/unit_tests.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b7c3af673e231aac68a57a2428ab4f18ba4a6221 |
--- /dev/null |
+++ b/sandbox/linux/tests/unit_tests.cc |
@@ -0,0 +1,326 @@ |
+// 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 <fcntl.h> |
+#include <poll.h> |
+#include <signal.h> |
+#include <stdio.h> |
+#include <sys/resource.h> |
+#include <sys/time.h> |
+#include <time.h> |
+#include <unistd.h> |
+ |
+#include "base/debug/leak_annotations.h" |
+#include "base/files/file_util.h" |
+#include "base/posix/eintr_wrapper.h" |
+#include "base/third_party/valgrind/valgrind.h" |
+#include "build/build_config.h" |
+#include "sandbox/linux/tests/unit_tests.h" |
+ |
+namespace { |
+std::string TestFailedMessage(const std::string& msg) { |
+ return msg.empty() ? std::string() : "Actual test failure: " + msg; |
+} |
+ |
+int GetSubProcessTimeoutTimeInSeconds() { |
+ // 10s ought to be enough for anybody. |
+ return 10; |
+} |
+ |
+// Returns the number of threads of the current process or -1. |
+int CountThreads() { |
+ struct stat task_stat; |
+ int task_d = stat("/proc/self/task", &task_stat); |
+ // task_stat.st_nlink should be the number of tasks + 2 (accounting for |
+ // "." and "..". |
+ if (task_d != 0 || task_stat.st_nlink < 3) |
+ return -1; |
+ const int num_threads = task_stat.st_nlink - 2; |
+ return num_threads; |
+} |
+ |
+} // namespace |
+ |
+namespace sandbox { |
+ |
+bool IsAndroid() { |
+#if defined(OS_ANDROID) |
+ return true; |
+#else |
+ return false; |
+#endif |
+} |
+ |
+bool IsArchitectureArm() { |
+#if defined(ARCH_CPU_ARM_FAMILY) |
+ return true; |
+#else |
+ return false; |
+#endif |
+} |
+ |
+// TODO(jln): figure out why base/.../dynamic_annotations.h's |
+// RunningOnValgrind() cannot link. |
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; } |
+ |
+static const int kExpectedValue = 42; |
+static const int kIgnoreThisTest = 43; |
+static const int kExitWithAssertionFailure = 1; |
+static const int kExitForTimeout = 2; |
+ |
+#if !defined(OS_ANDROID) |
+// This is due to StackDumpSignalHandler() performing _exit(1). |
+// TODO(jln): get rid of the collision with kExitWithAssertionFailure. |
+const int kExitAfterSIGSEGV = 1; |
+#endif |
+ |
+static void SigAlrmHandler(int) { |
+ const char failure_message[] = "Timeout reached!\n"; |
+ // Make sure that we never block here. |
+ if (!fcntl(2, F_SETFL, O_NONBLOCK)) { |
+ ignore_result(write(2, failure_message, sizeof(failure_message) - 1)); |
+ } |
+ _exit(kExitForTimeout); |
+} |
+ |
+// Set a timeout with a handler that will automatically fail the |
+// test. |
+static void SetProcessTimeout(int time_in_seconds) { |
+ struct sigaction act = {}; |
+ act.sa_handler = SigAlrmHandler; |
+ SANDBOX_ASSERT(sigemptyset(&act.sa_mask) == 0); |
+ act.sa_flags = 0; |
+ |
+ struct sigaction old_act; |
+ SANDBOX_ASSERT(sigaction(SIGALRM, &act, &old_act) == 0); |
+ |
+ // We don't implemenet signal chaining, so make sure that nothing else |
+ // is expecting to handle SIGALRM. |
+ SANDBOX_ASSERT((old_act.sa_flags & SA_SIGINFO) == 0); |
+ SANDBOX_ASSERT(old_act.sa_handler == SIG_DFL); |
+ sigset_t sigalrm_set; |
+ SANDBOX_ASSERT(sigemptyset(&sigalrm_set) == 0); |
+ SANDBOX_ASSERT(sigaddset(&sigalrm_set, SIGALRM) == 0); |
+ SANDBOX_ASSERT(sigprocmask(SIG_UNBLOCK, &sigalrm_set, NULL) == 0); |
+ SANDBOX_ASSERT(alarm(time_in_seconds) == 0); // There should be no previous |
+ // alarm. |
+} |
+ |
+// Runs a test in a sub-process. This is necessary for most of the code |
+// in the BPF sandbox, as it potentially makes global state changes and as |
+// it also tends to raise fatal errors, if the code has been used in an |
+// insecure manner. |
+void UnitTests::RunTestInProcess(SandboxTestRunner* test_runner, |
+ DeathCheck death, |
+ const void* death_aux) { |
+ CHECK(test_runner); |
+ // We need to fork(), so we can't be multi-threaded, as threads could hold |
+ // locks. |
+ int num_threads = CountThreads(); |
+#if !defined(THREAD_SANITIZER) |
+ const int kNumExpectedThreads = 1; |
+#else |
+ // Under TSAN, there is a special helper thread. It should be completely |
+ // invisible to our testing, so we ignore it. It should be ok to fork() |
+ // with this thread. It's currently buggy, but it's the best we can do until |
+ // there is a way to delay the start of the thread |
+ // (https://code.google.com/p/thread-sanitizer/issues/detail?id=19). |
+ const int kNumExpectedThreads = 2; |
+#endif |
+ |
+ // The kernel is at liberty to wake a thread id futex before updating /proc. |
+ // If another test running in the same process has stopped a thread, it may |
+ // appear as still running in /proc. |
+ // We poll /proc, with an exponential back-off. At most, we'll sleep around |
+ // 2^iterations nanoseconds in nanosleep(). |
+ for (unsigned int iteration = 0; iteration < 30; iteration++) { |
+ struct timespec ts = {0, 1L << iteration /* nanoseconds */}; |
+ PCHECK(0 == HANDLE_EINTR(nanosleep(&ts, &ts))); |
+ num_threads = CountThreads(); |
+ if (kNumExpectedThreads == num_threads) |
+ break; |
+ } |
+ |
+ ASSERT_EQ(kNumExpectedThreads, num_threads) |
+ << "Running sandbox tests with multiple threads " |
+ << "is not supported and will make the tests flaky."; |
+ int fds[2]; |
+ ASSERT_EQ(0, pipe(fds)); |
+ // Check that our pipe is not on one of the standard file descriptor. |
+ SANDBOX_ASSERT(fds[0] > 2 && fds[1] > 2); |
+ |
+ pid_t pid; |
+ ASSERT_LE(0, (pid = fork())); |
+ if (!pid) { |
+ // In child process |
+ // Redirect stderr to our pipe. This way, we can capture all error |
+ // messages, if we decide we want to do so in our tests. |
+ SANDBOX_ASSERT(dup2(fds[1], 2) == 2); |
+ SANDBOX_ASSERT(!close(fds[0])); |
+ SANDBOX_ASSERT(!close(fds[1])); |
+ |
+ // Don't set a timeout if running on Valgrind, since it's generally much |
+ // slower. |
+ if (!IsRunningOnValgrind()) { |
+ SetProcessTimeout(GetSubProcessTimeoutTimeInSeconds()); |
+ } |
+ |
+ // Disable core files. They are not very useful for our individual test |
+ // cases. |
+ struct rlimit no_core = {0}; |
+ setrlimit(RLIMIT_CORE, &no_core); |
+ |
+ test_runner->Run(); |
+ if (test_runner->ShouldCheckForLeaks()) { |
+#if defined(LEAK_SANITIZER) |
+ __lsan_do_leak_check(); |
+#endif |
+ } |
+ _exit(kExpectedValue); |
+ } |
+ |
+ close(fds[1]); |
+ std::vector<char> msg_buf; |
+ ssize_t rc; |
+ |
+ // Make sure read() will never block as we'll use poll() to |
+ // block with a timeout instead. |
+ const int fcntl_ret = fcntl(fds[0], F_SETFL, O_NONBLOCK); |
+ ASSERT_EQ(0, fcntl_ret); |
+ struct pollfd poll_fd = {fds[0], POLLIN | POLLRDHUP, 0}; |
+ |
+ int poll_ret; |
+ // We prefer the SIGALRM timeout to trigger in the child than this timeout |
+ // so we double the common value here. |
+ int poll_timeout = GetSubProcessTimeoutTimeInSeconds() * 2 * 1000; |
+ while ((poll_ret = poll(&poll_fd, 1, poll_timeout) > 0)) { |
+ const size_t kCapacity = 256; |
+ const size_t len = msg_buf.size(); |
+ msg_buf.resize(len + kCapacity); |
+ rc = HANDLE_EINTR(read(fds[0], &msg_buf[len], kCapacity)); |
+ msg_buf.resize(len + std::max(rc, static_cast<ssize_t>(0))); |
+ if (rc <= 0) |
+ break; |
+ } |
+ ASSERT_NE(poll_ret, -1) << "poll() failed"; |
+ ASSERT_NE(poll_ret, 0) << "Timeout while reading child state"; |
+ close(fds[0]); |
+ std::string msg(msg_buf.begin(), msg_buf.end()); |
+ |
+ int status = 0; |
+ int waitpid_returned = HANDLE_EINTR(waitpid(pid, &status, 0)); |
+ ASSERT_EQ(pid, waitpid_returned) << TestFailedMessage(msg); |
+ |
+ // At run-time, we sometimes decide that a test shouldn't actually |
+ // run (e.g. when testing sandbox features on a kernel that doesn't |
+ // have sandboxing support). When that happens, don't attempt to |
+ // call the "death" function, as it might be looking for a |
+ // death-test condition that would never have triggered. |
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != kIgnoreThisTest || |
+ !msg.empty()) { |
+ // We use gtest's ASSERT_XXX() macros instead of the DeathCheck |
+ // functions. This means, on failure, "return" is called. This |
+ // only works correctly, if the call of the "death" callback is |
+ // the very last thing in our function. |
+ death(status, msg, death_aux); |
+ } |
+} |
+ |
+void UnitTests::DeathSuccess(int status, const std::string& msg, const void*) { |
+ std::string details(TestFailedMessage(msg)); |
+ |
+ bool subprocess_terminated_normally = WIFEXITED(status); |
+ ASSERT_TRUE(subprocess_terminated_normally) << details; |
+ int subprocess_exit_status = WEXITSTATUS(status); |
+ ASSERT_EQ(kExpectedValue, subprocess_exit_status) << details; |
+ bool subprocess_exited_but_printed_messages = !msg.empty(); |
+ EXPECT_FALSE(subprocess_exited_but_printed_messages) << details; |
+} |
+ |
+void UnitTests::DeathSuccessAllowNoise(int status, |
+ const std::string& msg, |
+ const void*) { |
+ std::string details(TestFailedMessage(msg)); |
+ |
+ bool subprocess_terminated_normally = WIFEXITED(status); |
+ ASSERT_TRUE(subprocess_terminated_normally) << details; |
+ int subprocess_exit_status = WEXITSTATUS(status); |
+ ASSERT_EQ(kExpectedValue, subprocess_exit_status) << details; |
+} |
+ |
+void UnitTests::DeathMessage(int status, |
+ const std::string& msg, |
+ const void* aux) { |
+ std::string details(TestFailedMessage(msg)); |
+ const char* expected_msg = static_cast<const char*>(aux); |
+ |
+ bool subprocess_terminated_normally = WIFEXITED(status); |
+ ASSERT_TRUE(subprocess_terminated_normally) << "Exit status: " << status |
+ << " " << details; |
+ int subprocess_exit_status = WEXITSTATUS(status); |
+ ASSERT_EQ(1, subprocess_exit_status) << details; |
+ |
+ bool subprocess_exited_without_matching_message = |
+ msg.find(expected_msg) == std::string::npos; |
+ EXPECT_FALSE(subprocess_exited_without_matching_message) << details; |
+} |
+ |
+void UnitTests::DeathSEGVMessage(int status, |
+ const std::string& msg, |
+ const void* aux) { |
+ std::string details(TestFailedMessage(msg)); |
+ const char* expected_msg = static_cast<const char*>(aux); |
+ |
+#if defined(OS_ANDROID) |
+ const bool subprocess_got_sigsegv = |
+ WIFSIGNALED(status) && (SIGSEGV == WTERMSIG(status)); |
+#else |
+ const bool subprocess_got_sigsegv = |
+ WIFEXITED(status) && (kExitAfterSIGSEGV == WEXITSTATUS(status)); |
+#endif |
+ |
+ ASSERT_TRUE(subprocess_got_sigsegv) << "Exit status: " << status |
+ << " " << details; |
+ |
+ bool subprocess_exited_without_matching_message = |
+ msg.find(expected_msg) == std::string::npos; |
+ EXPECT_FALSE(subprocess_exited_without_matching_message) << details; |
+} |
+ |
+void UnitTests::DeathExitCode(int status, |
+ const std::string& msg, |
+ const void* aux) { |
+ int expected_exit_code = static_cast<int>(reinterpret_cast<intptr_t>(aux)); |
+ std::string details(TestFailedMessage(msg)); |
+ |
+ bool subprocess_terminated_normally = WIFEXITED(status); |
+ ASSERT_TRUE(subprocess_terminated_normally) << details; |
+ int subprocess_exit_status = WEXITSTATUS(status); |
+ ASSERT_EQ(expected_exit_code, subprocess_exit_status) << details; |
+} |
+ |
+void UnitTests::DeathBySignal(int status, |
+ const std::string& msg, |
+ const void* aux) { |
+ int expected_signo = static_cast<int>(reinterpret_cast<intptr_t>(aux)); |
+ std::string details(TestFailedMessage(msg)); |
+ |
+ bool subprocess_terminated_by_signal = WIFSIGNALED(status); |
+ ASSERT_TRUE(subprocess_terminated_by_signal) << details; |
+ int subprocess_signal_number = WTERMSIG(status); |
+ ASSERT_EQ(expected_signo, subprocess_signal_number) << details; |
+} |
+ |
+void UnitTests::AssertionFailure(const char* expr, const char* file, int line) { |
+ fprintf(stderr, "%s:%d:%s", file, line, expr); |
+ fflush(stderr); |
+ _exit(kExitWithAssertionFailure); |
+} |
+ |
+void UnitTests::IgnoreThisTest() { |
+ fflush(stderr); |
+ _exit(kIgnoreThisTest); |
+} |
+ |
+} // namespace |