#include <stdio.h>
#include <sys/resource.h>
#include <sys/time.h>
+#include <time.h>
#include <unistd.h>
-#include "base/file_util.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"
// TODO(jln): figure out why base/.../dynamic_annotations.h's
// RunningOnValgrind() cannot link.
-bool IsRunningOnValgrind() {
- return RUNNING_ON_VALGRIND;
-}
+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)) {
- if (write(2, failure_message, sizeof(failure_message) - 1) < 0) {
- }
- }
- _exit(kExitForTimeout);
+ 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
// 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(UnitTests::Test test, void *arg,
- DeathCheck death, const void *death_aux) {
+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)
+#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).
- num_threads--;
+ const int kNumExpectedThreads = 2;
#endif
- ASSERT_EQ(1, num_threads) << "Running sandbox tests with multiple threads "
- << "is not supported and will make the tests "
- << "flaky.\n";
+
+ // 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.
// Disable core files. They are not very useful for our individual test
// cases.
- struct rlimit no_core = { 0 };
+ struct rlimit no_core = {0};
setrlimit(RLIMIT_CORE, &no_core);
- test(arg);
+ test_runner->Run();
+ if (test_runner->ShouldCheckForLeaks()) {
+#if defined(LEAK_SANITIZER)
+ __lsan_do_leak_check();
+#endif
+ }
_exit(kExpectedValue);
}
- (void)HANDLE_EINTR(close(fds[1]));
+ 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(fcntl_ret, 0);
- struct pollfd poll_fd = { fds[0], POLLIN | POLLRDHUP, 0 };
+ 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
}
ASSERT_NE(poll_ret, -1) << "poll() failed";
ASSERT_NE(poll_ret, 0) << "Timeout while reading child state";
- (void)HANDLE_EINTR(close(fds[0]));
+ close(fds[0]);
std::string msg(msg_buf.begin(), msg_buf.end());
int status = 0;
}
}
-void UnitTests::DeathSuccess(int status, const std::string& msg,
- const void *) {
+void UnitTests::DeathSuccess(int status, const std::string& msg, const void*) {
std::string details(TestFailedMessage(msg));
bool subprocess_terminated_normally = WIFEXITED(status);
EXPECT_FALSE(subprocess_exited_but_printed_messages) << details;
}
-void UnitTests::DeathMessage(int status, const std::string& msg,
- const void *aux) {
+void UnitTests::DeathSuccessAllowNoise(int status,
+ const std::string& msg,
+ const void*) {
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) << details;
int subprocess_exit_status = WEXITSTATUS(status);
- ASSERT_EQ(kExitWithAssertionFailure, subprocess_exit_status) << details;
+ 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;
+ 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) {
+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(subprocess_exit_status, expected_exit_code) << details;
+ ASSERT_EQ(expected_exit_code, subprocess_exit_status) << details;
}
-void UnitTests::DeathBySignal(int status, const std::string& msg,
- const void *aux) {
+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(subprocess_signal_number, expected_signo) << details;
+ ASSERT_EQ(expected_signo, subprocess_signal_number) << details;
}
-void UnitTests::AssertionFailure(const char *expr, const char *file,
- int line) {
+void UnitTests::AssertionFailure(const char* expr, const char* file, int line) {
fprintf(stderr, "%s:%d:%s", file, line, expr);
fflush(stderr);
_exit(kExitWithAssertionFailure);