// 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