Skip to content

Commit

Permalink
Android sandbox: fix most BaselinePolicy tests
Browse files Browse the repository at this point in the history
On Android, the default signal handler is used and on SIGSEGV the process will
be terminated by the signal.
On Linux, the SIGSEGV signal handler terminates the process with _exit(1).

Unit tests that tested for SIGSEGV implicitly relied on the signal handler
terminating the process with _exit(1).

We create a new type of "death check" that specifically checks that the process
was terminated due to a SIGSEGV. However, on Linux this is still not very
reliable.

BUG=398611
R=mdempsky@chromium.org

Review URL: https://codereview.chromium.org/419683013

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@286576 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
jln@chromium.org committed Jul 30, 2014
1 parent 098647c commit cfb2258
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 14 deletions.
24 changes: 12 additions & 12 deletions sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -144,19 +144,19 @@ BPF_TEST_C(BaselinePolicy, CreateThread, BaselinePolicy) {

BPF_DEATH_TEST_C(BaselinePolicy,
DisallowedCloneFlagCrashes,
DEATH_MESSAGE(GetCloneErrorMessageContentForTests()),
DEATH_SEGV_MESSAGE(GetCloneErrorMessageContentForTests()),
BaselinePolicy) {
pid_t pid = syscall(__NR_clone, CLONE_THREAD | SIGCHLD);
HandlePostForkReturn(pid);
}

BPF_DEATH_TEST_C(BaselinePolicy,
DisallowedKillCrashes,
DEATH_MESSAGE(GetKillErrorMessageContentForTests()),
DEATH_SEGV_MESSAGE(GetKillErrorMessageContentForTests()),
BaselinePolicy) {
BPF_ASSERT_NE(1, getpid());
kill(1, 0);
_exit(1);
_exit(0);
}

BPF_TEST_C(BaselinePolicy, CanKillSelf, BaselinePolicy) {
Expand All @@ -179,7 +179,7 @@ BPF_TEST_C(BaselinePolicy, Socketpair, BaselinePolicy) {
#if defined(__x86_64__) || defined(__arm__)
BPF_DEATH_TEST_C(BaselinePolicy,
SocketpairWrongDomain,
DEATH_MESSAGE(GetErrorMessageContentForTests()),
DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()),
BaselinePolicy) {
int sv[2];
ignore_result(socketpair(AF_INET, SOCK_STREAM, 0, sv));
Expand Down Expand Up @@ -215,13 +215,13 @@ BPF_TEST_C(BaselinePolicy, EPERM_getcwd, BaselinePolicy) {
// the machine. Some thoughts have been given when hand-picking the system
// calls below to limit any potential side effects outside of the current
// process.
#define TEST_BASELINE_SIGSYS(sysno) \
BPF_DEATH_TEST_C(BaselinePolicy, \
SIGSYS_##sysno, \
DEATH_MESSAGE(GetErrorMessageContentForTests()), \
BaselinePolicy) { \
syscall(sysno, 0, 0, 0, 0, 0, 0); \
_exit(1); \
#define TEST_BASELINE_SIGSYS(sysno) \
BPF_DEATH_TEST_C(BaselinePolicy, \
SIGSYS_##sysno, \
DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), \
BaselinePolicy) { \
syscall(sysno, 0, 0, 0, 0, 0, 0); \
_exit(1); \
}

TEST_BASELINE_SIGSYS(__NR_syslog);
Expand Down Expand Up @@ -277,7 +277,7 @@ BPF_TEST_C(BaselinePolicy, PrctlDumpable, BaselinePolicy) {

BPF_DEATH_TEST_C(BaselinePolicy,
PrctlSigsys,
DEATH_MESSAGE(GetPrctlErrorMessageContentForTests()),
DEATH_SEGV_MESSAGE(GetPrctlErrorMessageContentForTests()),
BaselinePolicy) {
prctl(PR_CAPBSET_READ, 0, 0, 0, 0);
_exit(1);
Expand Down
32 changes: 30 additions & 2 deletions sandbox/linux/tests/unit_tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ 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.
Expand Down Expand Up @@ -250,9 +256,31 @@ void UnitTests::DeathMessage(int status,
const char* expected_msg = static_cast<const char*>(aux);

bool subprocess_terminated_normally = WIFEXITED(status);
ASSERT_TRUE(subprocess_terminated_normally) << details;
ASSERT_TRUE(subprocess_terminated_normally) << status;
int subprocess_exit_status = WEXITSTATUS(status);
ASSERT_EQ(kExitWithAssertionFailure, subprocess_exit_status) << details;
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) << status;

bool subprocess_exited_without_matching_message =
msg.find(expected_msg) == std::string::npos;
EXPECT_FALSE(subprocess_exited_without_matching_message) << details;
Expand Down
13 changes: 13 additions & 0 deletions sandbox/linux/tests/unit_tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ bool IsRunningOnValgrind();
#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))
Expand Down Expand Up @@ -148,6 +151,16 @@ class UnitTests {
// 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
Expand Down
12 changes: 12 additions & 0 deletions sandbox/linux/tests/unit_tests_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ SANDBOX_DEATH_TEST(UnitTests,
_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!";
}
Expand Down

0 comments on commit cfb2258

Please sign in to comment.