summaryrefslogtreecommitdiff
path: root/sandbox/linux/tests
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/linux/tests')
-rw-r--r--sandbox/linux/tests/main.cc82
-rw-r--r--sandbox/linux/tests/sandbox_test_runner.cc19
-rw-r--r--sandbox/linux/tests/sandbox_test_runner.h30
-rw-r--r--sandbox/linux/tests/sandbox_test_runner_function_pointer.cc25
-rw-r--r--sandbox/linux/tests/sandbox_test_runner_function_pointer.h26
-rw-r--r--sandbox/linux/tests/scoped_temporary_file.cc35
-rw-r--r--sandbox/linux/tests/scoped_temporary_file.h30
-rw-r--r--sandbox/linux/tests/scoped_temporary_file_unittest.cc76
-rw-r--r--sandbox/linux/tests/test_utils.cc42
-rw-r--r--sandbox/linux/tests/test_utils.h29
-rw-r--r--sandbox/linux/tests/test_utils_unittest.cc24
-rw-r--r--sandbox/linux/tests/unit_tests.cc354
-rw-r--r--sandbox/linux/tests/unit_tests.h201
-rw-r--r--sandbox/linux/tests/unit_tests_unittest.cc62
14 files changed, 1035 insertions, 0 deletions
diff --git a/sandbox/linux/tests/main.cc b/sandbox/linux/tests/main.cc
new file mode 100644
index 0000000000..caeddee32c
--- /dev/null
+++ b/sandbox/linux/tests/main.cc
@@ -0,0 +1,82 @@
+// 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 "base/at_exit.h"
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/test/test_suite.h"
+#include "build/build_config.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace sandbox {
+namespace {
+
+// Check for leaks in our tests.
+void RunPostTestsChecks(const base::FilePath& orig_cwd) {
+ if (TestUtils::CurrentProcessHasChildren()) {
+ LOG(FATAL) << "One of the tests created a child that was not waited for. "
+ << "Please, clean up after your tests!";
+ }
+
+ base::FilePath cwd;
+ CHECK(GetCurrentDirectory(&cwd));
+ if (orig_cwd != cwd) {
+ LOG(FATAL) << "One of the tests changed the current working directory. "
+ << "Please, clean up after your tests!";
+ }
+}
+
+} // namespace
+} // namespace sandbox
+
+#if !defined(SANDBOX_USES_BASE_TEST_SUITE)
+void UnitTestAssertHandler(const std::string& str) {
+ _exit(1);
+}
+#endif
+
+int main(int argc, char* argv[]) {
+ base::CommandLine::Init(argc, argv);
+ std::string client_func;
+#if defined(SANDBOX_USES_BASE_TEST_SUITE)
+ client_func = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kTestChildProcess);
+#endif
+ if (!client_func.empty()) {
+ base::AtExitManager exit_manager;
+ return multi_process_function_list::InvokeChildProcessTest(client_func);
+ }
+
+ base::FilePath orig_cwd;
+ CHECK(GetCurrentDirectory(&orig_cwd));
+
+#if !defined(SANDBOX_USES_BASE_TEST_SUITE)
+ // The use of Callbacks requires an AtExitManager.
+ base::AtExitManager exit_manager;
+ testing::InitGoogleTest(&argc, argv);
+ // Death tests rely on LOG(FATAL) triggering an exit (the default behavior is
+ // SIGABRT). The normal test launcher does this at initialization, but since
+ // we still do not use this on Android, we must install the handler ourselves.
+ logging::SetLogAssertHandler(UnitTestAssertHandler);
+#endif
+ // 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";
+#if !defined(SANDBOX_USES_BASE_TEST_SUITE)
+ int tests_result = RUN_ALL_TESTS();
+#else
+ int tests_result = base::RunUnitTestsUsingBaseTestSuite(argc, argv);
+#endif
+
+ sandbox::RunPostTestsChecks(orig_cwd);
+ return tests_result;
+}
diff --git a/sandbox/linux/tests/sandbox_test_runner.cc b/sandbox/linux/tests/sandbox_test_runner.cc
new file mode 100644
index 0000000000..b099b97289
--- /dev/null
+++ b/sandbox/linux/tests/sandbox_test_runner.cc
@@ -0,0 +1,19 @@
+// Copyright 2014 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 "sandbox/linux/tests/sandbox_test_runner.h"
+
+namespace sandbox {
+
+SandboxTestRunner::SandboxTestRunner() {
+}
+
+SandboxTestRunner::~SandboxTestRunner() {
+}
+
+bool SandboxTestRunner::ShouldCheckForLeaks() const {
+ return true;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/sandbox_test_runner.h b/sandbox/linux/tests/sandbox_test_runner.h
new file mode 100644
index 0000000000..3155b74008
--- /dev/null
+++ b/sandbox/linux/tests/sandbox_test_runner.h
@@ -0,0 +1,30 @@
+// Copyright 2014 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_TESTS_SANDBOX_TEST_RUNNER_H_
+#define SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_H_
+
+#include "base/macros.h"
+
+namespace sandbox {
+
+// A simple "runner" class to implement tests.
+class SandboxTestRunner {
+ public:
+ SandboxTestRunner();
+ virtual ~SandboxTestRunner();
+
+ virtual void Run() = 0;
+
+ // Override to decide whether or not to check for leaks with LSAN
+ // (if built with LSAN and LSAN is enabled).
+ virtual bool ShouldCheckForLeaks() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SandboxTestRunner);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_H_
diff --git a/sandbox/linux/tests/sandbox_test_runner_function_pointer.cc b/sandbox/linux/tests/sandbox_test_runner_function_pointer.cc
new file mode 100644
index 0000000000..69e05ac4e0
--- /dev/null
+++ b/sandbox/linux/tests/sandbox_test_runner_function_pointer.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 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 "sandbox/linux/tests/sandbox_test_runner_function_pointer.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+namespace sandbox {
+
+SandboxTestRunnerFunctionPointer::SandboxTestRunnerFunctionPointer(
+ void (*function_to_run)(void))
+ : function_to_run_(function_to_run) {
+}
+
+SandboxTestRunnerFunctionPointer::~SandboxTestRunnerFunctionPointer() {
+}
+
+void SandboxTestRunnerFunctionPointer::Run() {
+ DCHECK(function_to_run_);
+ function_to_run_();
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/sandbox_test_runner_function_pointer.h b/sandbox/linux/tests/sandbox_test_runner_function_pointer.h
new file mode 100644
index 0000000000..cadd07c248
--- /dev/null
+++ b/sandbox/linux/tests/sandbox_test_runner_function_pointer.h
@@ -0,0 +1,26 @@
+// Copyright 2014 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_TESTS_SANDBOX_TEST_RUNNER_FUNCTION_POINTER_H_
+#define SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_FUNCTION_POINTER_H_
+
+#include "base/macros.h"
+#include "sandbox/linux/tests/sandbox_test_runner.h"
+
+namespace sandbox {
+
+class SandboxTestRunnerFunctionPointer : public SandboxTestRunner {
+ public:
+ SandboxTestRunnerFunctionPointer(void (*function_to_run)(void));
+ ~SandboxTestRunnerFunctionPointer() override;
+ void Run() override;
+
+ private:
+ void (*function_to_run_)(void);
+ DISALLOW_COPY_AND_ASSIGN(SandboxTestRunnerFunctionPointer);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER__FUNCTION_POINTER_H_
diff --git a/sandbox/linux/tests/scoped_temporary_file.cc b/sandbox/linux/tests/scoped_temporary_file.cc
new file mode 100644
index 0000000000..1f2d66fd6b
--- /dev/null
+++ b/sandbox/linux/tests/scoped_temporary_file.cc
@@ -0,0 +1,35 @@
+// Copyright 2014 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 "sandbox/linux/tests/scoped_temporary_file.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+
+namespace sandbox {
+
+ScopedTemporaryFile::ScopedTemporaryFile() : fd_(-1) {
+#if defined(OS_ANDROID)
+ static const char file_template[] = "/data/local/tmp/ScopedTempFileXXXXXX";
+#else
+ static const char file_template[] = "/tmp/ScopedTempFileXXXXXX";
+#endif // defined(OS_ANDROID)
+ static_assert(sizeof(full_file_name_) >= sizeof(file_template),
+ "full_file_name is not large enough");
+ memcpy(full_file_name_, file_template, sizeof(file_template));
+ fd_ = mkstemp(full_file_name_);
+ CHECK_LE(0, fd_);
+}
+
+ScopedTemporaryFile::~ScopedTemporaryFile() {
+ CHECK_EQ(0, unlink(full_file_name_));
+ CHECK_EQ(0, IGNORE_EINTR(close(fd_)));
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/scoped_temporary_file.h b/sandbox/linux/tests/scoped_temporary_file.h
new file mode 100644
index 0000000000..0734130055
--- /dev/null
+++ b/sandbox/linux/tests/scoped_temporary_file.h
@@ -0,0 +1,30 @@
+// Copyright 2014 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_TESTS_SCOPED_TEMPORARY_FILE_H_
+#define SANDBOX_LINUX_TESTS_SCOPED_TEMPORARY_FILE_H_
+
+#include "base/macros.h"
+
+namespace sandbox {
+// Creates and open a temporary file on creation and closes
+// and removes it on destruction.
+// Unlike base/ helpers, this does not require JNI on Android.
+class ScopedTemporaryFile {
+ public:
+ ScopedTemporaryFile();
+ ~ScopedTemporaryFile();
+
+ int fd() const { return fd_; }
+ const char* full_file_name() const { return full_file_name_; }
+
+ private:
+ int fd_;
+ char full_file_name_[128];
+ DISALLOW_COPY_AND_ASSIGN(ScopedTemporaryFile);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_TESTS_SCOPED_TEMPORARY_FILE_H_
diff --git a/sandbox/linux/tests/scoped_temporary_file_unittest.cc b/sandbox/linux/tests/scoped_temporary_file_unittest.cc
new file mode 100644
index 0000000000..44a2ecb1ae
--- /dev/null
+++ b/sandbox/linux/tests/scoped_temporary_file_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright 2014 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 "sandbox/linux/tests/scoped_temporary_file.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+bool FullWrite(int fd, const char* buffer, size_t count) {
+ while (count > 0) {
+ const ssize_t transfered = HANDLE_EINTR(write(fd, buffer, count));
+ if (transfered <= 0 || static_cast<size_t>(transfered) > count) {
+ return false;
+ }
+ count -= transfered;
+ buffer += transfered;
+ }
+ return true;
+}
+
+bool FullRead(int fd, char* buffer, size_t count) {
+ while (count > 0) {
+ const ssize_t transfered = HANDLE_EINTR(read(fd, buffer, count));
+ if (transfered <= 0 || static_cast<size_t>(transfered) > count) {
+ return false;
+ }
+ count -= transfered;
+ buffer += transfered;
+ }
+ return true;
+}
+
+TEST(ScopedTemporaryFile, Basics) {
+ std::string temp_file_name;
+ {
+ ScopedTemporaryFile temp_file_1;
+ const char kTestString[] = "This is a test";
+ ASSERT_LE(0, temp_file_1.fd());
+
+ temp_file_name = temp_file_1.full_file_name();
+ base::ScopedFD temp_file_2(open(temp_file_1.full_file_name(), O_RDONLY));
+ ASSERT_TRUE(temp_file_2.is_valid());
+
+ ASSERT_TRUE(FullWrite(temp_file_1.fd(), kTestString, sizeof(kTestString)));
+
+ char test_string_read[sizeof(kTestString)] = {0};
+ ASSERT_TRUE(FullRead(
+ temp_file_2.get(), test_string_read, sizeof(test_string_read)));
+ ASSERT_EQ(0, memcmp(kTestString, test_string_read, sizeof(kTestString)));
+ }
+
+ errno = 0;
+ struct stat buf;
+ ASSERT_EQ(-1, stat(temp_file_name.c_str(), &buf));
+ ASSERT_EQ(ENOENT, errno);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/test_utils.cc b/sandbox/linux/tests/test_utils.cc
new file mode 100644
index 0000000000..747bad27a5
--- /dev/null
+++ b/sandbox/linux/tests/test_utils.cc
@@ -0,0 +1,42 @@
+// Copyright 2014 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 "sandbox/linux/tests/test_utils.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace sandbox {
+
+bool TestUtils::CurrentProcessHasChildren() {
+ siginfo_t process_info;
+ int ret = HANDLE_EINTR(
+ waitid(P_ALL, 0, &process_info, WEXITED | WNOHANG | WNOWAIT));
+ if (-1 == ret) {
+ PCHECK(ECHILD == errno);
+ return false;
+ } else {
+ return true;
+ }
+}
+
+void TestUtils::HandlePostForkReturn(pid_t pid) {
+ const int kChildExitCode = 1;
+ if (pid > 0) {
+ int status = 0;
+ PCHECK(pid == HANDLE_EINTR(waitpid(pid, &status, 0)));
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(kChildExitCode, WEXITSTATUS(status));
+ } else if (pid == 0) {
+ _exit(kChildExitCode);
+ }
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/test_utils.h b/sandbox/linux/tests/test_utils.h
new file mode 100644
index 0000000000..7cf9749fe4
--- /dev/null
+++ b/sandbox/linux/tests/test_utils.h
@@ -0,0 +1,29 @@
+// Copyright 2014 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_TESTS_TEST_UTILS_H_
+#define SANDBOX_LINUX_TESTS_TEST_UTILS_H_
+
+#include <sys/types.h>
+
+#include "base/macros.h"
+
+namespace sandbox {
+
+// This class provide small helpers to help writing tests.
+class TestUtils {
+ public:
+ static bool CurrentProcessHasChildren();
+ // |pid| is the return value of a fork()-like call. This
+ // makes sure that if fork() succeeded the child exits
+ // and the parent waits for it.
+ static void HandlePostForkReturn(pid_t pid);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TestUtils);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_TESTS_TEST_UTILS_H_
diff --git a/sandbox/linux/tests/test_utils_unittest.cc b/sandbox/linux/tests/test_utils_unittest.cc
new file mode 100644
index 0000000000..0f86e616e9
--- /dev/null
+++ b/sandbox/linux/tests/test_utils_unittest.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 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 "sandbox/linux/tests/test_utils.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+// Check that HandlePostForkReturn works.
+TEST(TestUtils, HandlePostForkReturn) {
+ pid_t pid = fork();
+ TestUtils::HandlePostForkReturn(pid);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/unit_tests.cc b/sandbox/linux/tests/unit_tests.cc
new file mode 100644
index 0000000000..4973c41fbd
--- /dev/null
+++ b/sandbox/linux/tests/unit_tests.cc
@@ -0,0 +1,354 @@
+// 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/types.h>
+#include <sys/wait.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"
+
+// Specifically, PNaCl toolchain does not have this flag.
+#if !defined(POLLRDHUP)
+#define POLLRDHUP 0x2000
+#endif
+
+namespace {
+std::string TestFailedMessage(const std::string& msg) {
+ return msg.empty() ? std::string() : "Actual test failure: " + msg;
+}
+
+int GetSubProcessTimeoutTimeInSeconds() {
+ // Previously 10s, but that timed out (just) on Chromecast.
+ return 12;
+}
+
+// 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;
+#if !defined(OS_NACL_NONSFI)
+static const int kExitForTimeout = 2;
+#endif
+
+#if defined(SANDBOX_USES_BASE_TEST_SUITE)
+// This is due to StackDumpSignalHandler() performing _exit(1).
+// TODO(jln): get rid of the collision with kExitWithAssertionFailure.
+const int kExitAfterSIGSEGV = 1;
+#endif
+
+// PNaCl toolchain's signal ABIs are incompatible with Linux's.
+// So, for simplicity, just drop the "timeout" feature from unittest framework
+// with relying on the buildbot's timeout feature.
+#if !defined(OS_NACL_NONSFI)
+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.
+}
+#endif // !defined(OS_NACL_NONSFI)
+
+// 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()) {
+#if !defined(OS_NACL_NONSFI)
+ SetProcessTimeout(GetSubProcessTimeoutTimeInSeconds());
+#endif
+ }
+
+ // 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;
+
+// In official builds CHECK messages are dropped, so look for SIGABRT.
+// See https://code.google.com/p/chromium/issues/detail?id=437312
+#if defined(OFFICIAL_BUILD) && defined(NDEBUG) && !defined(OS_ANDROID)
+ if (subprocess_exited_without_matching_message) {
+ static const char kSigAbortMessage[] = "Received signal 6";
+ subprocess_exited_without_matching_message =
+ msg.find(kSigAbortMessage) == std::string::npos;
+ }
+#endif
+ 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(SANDBOX_USES_BASE_TEST_SUITE)
+ const bool subprocess_got_sigsegv =
+ WIFSIGNALED(status) && (SIGSEGV == WTERMSIG(status));
+#else
+ // This hack is required when a signal handler is installed
+ // for SEGV that will _exit(1).
+ 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
diff --git a/sandbox/linux/tests/unit_tests.h b/sandbox/linux/tests/unit_tests.h
new file mode 100644
index 0000000000..5a7116e932
--- /dev/null
+++ b/sandbox/linux/tests/unit_tests.h
@@ -0,0 +1,201 @@
+// 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_TESTS_UNIT_TESTS_H_
+#define SANDBOX_LINUX_TESTS_UNIT_TESTS_H_
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "sandbox/linux/tests/sandbox_test_runner_function_pointer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Has this been compiled to run on Android?
+bool IsAndroid();
+
+bool IsArchitectureArm();
+
+// Is Valgrind currently being used?
+bool IsRunningOnValgrind();
+
+#if defined(ADDRESS_SANITIZER)
+#define DISABLE_ON_ASAN(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_ASAN(test_name) test_name
+#endif // defined(ADDRESS_SANITIZER)
+
+#if defined(LEAK_SANITIZER)
+#define DISABLE_ON_LSAN(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_LSAN(test_name) test_name
+#endif
+
+#if defined(THREAD_SANITIZER)
+#define DISABLE_ON_TSAN(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_TSAN(test_name) test_name
+#endif // defined(THREAD_SANITIZER)
+
+#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
+ defined(THREAD_SANITIZER) || defined(LEAK_SANITIZER) || \
+ defined(UNDEFINED_SANITIZER) || defined(SANITIZER_COVERAGE)
+#define DISABLE_ON_SANITIZERS(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_SANITIZERS(test_name) test_name
+#endif
+
+#if defined(OS_ANDROID)
+#define DISABLE_ON_ANDROID(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_ANDROID(test_name) test_name
+#endif
+
+// While it is perfectly OK for a complex test to provide its own DeathCheck
+// function. Most death tests have very simple requirements. These tests should
+// use one of the predefined DEATH_XXX macros as an argument to
+// SANDBOX_DEATH_TEST(). You can check for a (sub-)string in the output of the
+// test, for a particular exit code, or for a particular death signal.
+// NOTE: If you do decide to write your own DeathCheck, make sure to use
+// gtests's ASSERT_XXX() macros instead of SANDBOX_ASSERT(). See
+// unit_tests.cc for examples.
+#define DEATH_SUCCESS() sandbox::UnitTests::DeathSuccess, NULL
+#define DEATH_SUCCESS_ALLOW_NOISE() \
+ sandbox::UnitTests::DeathSuccessAllowNoise, NULL
+#define DEATH_MESSAGE(msg) \
+ sandbox::UnitTests::DeathMessage, \
+ static_cast<const void*>(static_cast<const char*>(msg))
+#define DEATH_SEGV_MESSAGE(msg) \
+ sandbox::UnitTests::DeathSEGVMessage, \
+ static_cast<const void*>(static_cast<const char*>(msg))
+#define DEATH_EXIT_CODE(rc) \
+ sandbox::UnitTests::DeathExitCode, \
+ reinterpret_cast<void*>(static_cast<intptr_t>(rc))
+#define DEATH_BY_SIGNAL(s) \
+ sandbox::UnitTests::DeathBySignal, \
+ reinterpret_cast<void*>(static_cast<intptr_t>(s))
+
+// A SANDBOX_DEATH_TEST is just like a SANDBOX_TEST (see below), but it assumes
+// that the test actually dies. The death test only passes if the death occurs
+// in the expected fashion, as specified by "death" and "death_aux". These two
+// parameters are typically set to one of the DEATH_XXX() macros.
+#define SANDBOX_DEATH_TEST(test_case_name, test_name, death) \
+ void TEST_##test_name(void); \
+ TEST(test_case_name, test_name) { \
+ SandboxTestRunnerFunctionPointer sandbox_test_runner(TEST_##test_name); \
+ sandbox::UnitTests::RunTestInProcess(&sandbox_test_runner, death); \
+ } \
+ void TEST_##test_name(void)
+
+// Define a new test case that runs inside of a GTest death test. This is
+// necessary, as most of our tests by definition make global and irreversible
+// changes to the system (i.e. they install a sandbox). GTest provides death
+// tests as a tool to isolate global changes from the rest of the tests.
+#define SANDBOX_TEST(test_case_name, test_name) \
+ SANDBOX_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS())
+
+// SANDBOX_TEST_ALLOW_NOISE is just like SANDBOX_TEST, except it does not
+// consider log error messages printed by the test to be test failures.
+#define SANDBOX_TEST_ALLOW_NOISE(test_case_name, test_name) \
+ SANDBOX_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS_ALLOW_NOISE())
+
+// Simple assertion macro that is compatible with running inside of a death
+// test. We unfortunately cannot use any of the GTest macros.
+#define SANDBOX_STR(x) #x
+#define SANDBOX_ASSERT(expr) \
+ ((expr) ? static_cast<void>(0) : sandbox::UnitTests::AssertionFailure( \
+ SANDBOX_STR(expr), __FILE__, __LINE__))
+
+#define SANDBOX_ASSERT_EQ(x, y) SANDBOX_ASSERT((x) == (y))
+#define SANDBOX_ASSERT_NE(x, y) SANDBOX_ASSERT((x) != (y))
+#define SANDBOX_ASSERT_LT(x, y) SANDBOX_ASSERT((x) < (y))
+#define SANDBOX_ASSERT_GT(x, y) SANDBOX_ASSERT((x) > (y))
+#define SANDBOX_ASSERT_LE(x, y) SANDBOX_ASSERT((x) <= (y))
+#define SANDBOX_ASSERT_GE(x, y) SANDBOX_ASSERT((x) >= (y))
+
+// This class allows to run unittests in their own process. The main method is
+// RunTestInProcess().
+class UnitTests {
+ public:
+ typedef void (*DeathCheck)(int status,
+ const std::string& msg,
+ const void* aux);
+
+ // Runs a test inside a short-lived process. Do not call this function
+ // directly. It is automatically invoked by SANDBOX_TEST(). Most sandboxing
+ // functions make global irreversible changes to the execution environment
+ // and must therefore execute in their own isolated process.
+ // |test_runner| must implement the SandboxTestRunner interface and will run
+ // in a subprocess.
+ // Note: since the child process (created with fork()) will never return from
+ // RunTestInProcess(), |test_runner| is guaranteed to exist for the lifetime
+ // of the child process.
+ static void RunTestInProcess(SandboxTestRunner* test_runner,
+ DeathCheck death,
+ const void* death_aux);
+
+ // Report a useful error message and terminate the current SANDBOX_TEST().
+ // Calling this function from outside a SANDBOX_TEST() is unlikely to do
+ // anything useful.
+ static void AssertionFailure(const char* expr, const char* file, int line);
+
+ // Sometimes we determine at run-time that a test should be disabled.
+ // Call this method if we want to return from a test and completely
+ // ignore its results.
+ // You should not call this method, if the test already ran any test-relevant
+ // code. Most notably, you should not call it, you already wrote any messages
+ // to stderr.
+ static void IgnoreThisTest();
+
+ // A DeathCheck method that verifies that the test completed succcessfully.
+ // This is the default test mode for SANDBOX_TEST(). The "aux" parameter
+ // of this DeathCheck is unused (and thus unnamed)
+ static void DeathSuccess(int status, const std::string& msg, const void*);
+
+ // A DeathCheck method that verifies that the test completed succcessfully
+ // allowing for log error messages.
+ static void DeathSuccessAllowNoise(int status,
+ const std::string& msg,
+ const void*);
+
+ // A DeathCheck method that verifies that the test completed with error
+ // code "1" and printed a message containing a particular substring. The
+ // "aux" pointer should point to a C-string containing the expected error
+ // message. This method is useful for checking assertion failures such as
+ // in SANDBOX_ASSERT() and/or SANDBOX_DIE().
+ static void DeathMessage(int status, const std::string& msg, const void* aux);
+
+ // Like DeathMessage() but the process must be terminated with a segmentation
+ // fault.
+ // Implementation detail: On Linux (but not on Android), this does check for
+ // the return value of our default signal handler rather than for the actual
+ // reception of a SIGSEGV.
+ // TODO(jln): make this more robust.
+ static void DeathSEGVMessage(int status,
+ const std::string& msg,
+ const void* aux);
+
+ // A DeathCheck method that verifies that the test completed with a
+ // particular exit code. If the test output any messages to stderr, they are
+ // silently ignored. The expected exit code should be passed in by
+ // casting the its "int" value to a "void *", which is then used for "aux".
+ static void DeathExitCode(int status,
+ const std::string& msg,
+ const void* aux);
+
+ // A DeathCheck method that verifies that the test was terminated by a
+ // particular signal. If the test output any messages to stderr, they are
+ // silently ignore. The expected signal number should be passed in by
+ // casting the its "int" value to a "void *", which is then used for "aux".
+ static void DeathBySignal(int status,
+ const std::string& msg,
+ const void* aux);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(UnitTests);
+};
+
+} // namespace
+
+#endif // SANDBOX_LINUX_TESTS_UNIT_TESTS_H_
diff --git a/sandbox/linux/tests/unit_tests_unittest.cc b/sandbox/linux/tests/unit_tests_unittest.cc
new file mode 100644
index 0000000000..57799b14c0
--- /dev/null
+++ b/sandbox/linux/tests/unit_tests_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 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 <signal.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+namespace {
+
+// Let's not use any of the "magic" values used internally in unit_tests.cc,
+// such as kExpectedValue.
+const int kExpectedExitCode = 100;
+
+SANDBOX_DEATH_TEST(UnitTests,
+ DeathExitCode,
+ DEATH_EXIT_CODE(kExpectedExitCode)) {
+ _exit(kExpectedExitCode);
+}
+
+const int kExpectedSignalNumber = SIGKILL;
+
+SANDBOX_DEATH_TEST(UnitTests,
+ DeathBySignal,
+ DEATH_BY_SIGNAL(kExpectedSignalNumber)) {
+ raise(kExpectedSignalNumber);
+}
+
+SANDBOX_DEATH_TEST(UnitTests,
+ DeathWithMessage,
+ DEATH_MESSAGE("Hello")) {
+ LOG(ERROR) << "Hello";
+ _exit(1);
+}
+
+SANDBOX_DEATH_TEST(UnitTests,
+ SEGVDeathWithMessage,
+ DEATH_SEGV_MESSAGE("Hello")) {
+ LOG(ERROR) << "Hello";
+ while (1) {
+ volatile char* addr = reinterpret_cast<volatile char*>(NULL);
+ *addr = '\0';
+ }
+
+ _exit(2);
+}
+
+SANDBOX_TEST_ALLOW_NOISE(UnitTests, NoisyTest) {
+ LOG(ERROR) << "The cow says moo!";
+}
+
+} // namespace
+
+} // namespace sandbox