debuggerd_test: add capability test.
Also, remove the dependency on crasher. Bug: http://b/35100921 Bug: http://b/35241370 Test: /data/nativetest/debuggerd_test/debuggerd_test32 Test: /data/nativetest64/debuggerd_test/debuggerd_test64 Change-Id: I318f6de764d435251417953bf175ba321b59981f
This commit is contained in:
parent
892b158af8
commit
502cfd22ba
|
@ -17,6 +17,7 @@
|
|||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
@ -24,6 +25,8 @@
|
|||
#include <regex>
|
||||
#include <thread>
|
||||
|
||||
#include <android/set_abort_message.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
|
@ -40,10 +43,8 @@ using namespace std::chrono_literals;
|
|||
using android::base::unique_fd;
|
||||
|
||||
#if defined(__LP64__)
|
||||
#define CRASHER_PATH "/system/bin/crasher64"
|
||||
#define ARCH_SUFFIX "64"
|
||||
#else
|
||||
#define CRASHER_PATH "/system/bin/crasher"
|
||||
#define ARCH_SUFFIX ""
|
||||
#endif
|
||||
|
||||
|
@ -179,23 +180,14 @@ void CrasherTest::StartProcess(std::function<void()> function) {
|
|||
if (crasher_pid == -1) {
|
||||
FAIL() << "fork failed: " << strerror(errno);
|
||||
} else if (crasher_pid == 0) {
|
||||
unique_fd devnull(open("/dev/null", O_WRONLY));
|
||||
dup2(crasher_read_pipe.get(), STDIN_FILENO);
|
||||
dup2(devnull.get(), STDOUT_FILENO);
|
||||
dup2(devnull.get(), STDERR_FILENO);
|
||||
char dummy;
|
||||
crasher_pipe.reset();
|
||||
TEMP_FAILURE_RETRY(read(crasher_read_pipe.get(), &dummy, 1));
|
||||
function();
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void CrasherTest::StartCrasher(const std::string& crash_type) {
|
||||
std::string type = "wait-" + crash_type;
|
||||
StartProcess([type]() {
|
||||
execl(CRASHER_PATH, CRASHER_PATH, type.c_str(), nullptr);
|
||||
exit(errno);
|
||||
});
|
||||
}
|
||||
|
||||
void CrasherTest::FinishCrasher() {
|
||||
if (crasher_pipe == -1) {
|
||||
FAIL() << "crasher pipe uninitialized";
|
||||
|
@ -249,7 +241,10 @@ static void ConsumeFd(unique_fd fd, std::string* output) {
|
|||
TEST_F(CrasherTest, smoke) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
StartCrasher("SIGSEGV");
|
||||
StartProcess([]() {
|
||||
*reinterpret_cast<volatile char*>(0xdead) = '1';
|
||||
});
|
||||
|
||||
StartIntercept(&output_fd);
|
||||
FinishCrasher();
|
||||
AssertDeath(SIGSEGV);
|
||||
|
@ -265,7 +260,9 @@ TEST_F(CrasherTest, smoke) {
|
|||
TEST_F(CrasherTest, abort) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
StartCrasher("abort");
|
||||
StartProcess([]() {
|
||||
abort();
|
||||
});
|
||||
StartIntercept(&output_fd);
|
||||
FinishCrasher();
|
||||
AssertDeath(SIGABRT);
|
||||
|
@ -281,7 +278,9 @@ TEST_F(CrasherTest, abort) {
|
|||
TEST_F(CrasherTest, signal) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
StartCrasher("abort");
|
||||
StartProcess([]() {
|
||||
abort();
|
||||
});
|
||||
StartIntercept(&output_fd);
|
||||
|
||||
// Wait for a bit, or we might end up killing the process before the signal
|
||||
|
@ -303,7 +302,10 @@ TEST_F(CrasherTest, signal) {
|
|||
TEST_F(CrasherTest, abort_message) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
StartCrasher("smash-stack");
|
||||
StartProcess([]() {
|
||||
android_set_abort_message("abort message goes here");
|
||||
abort();
|
||||
});
|
||||
StartIntercept(&output_fd);
|
||||
FinishCrasher();
|
||||
AssertDeath(SIGABRT);
|
||||
|
@ -313,13 +315,15 @@ TEST_F(CrasherTest, abort_message) {
|
|||
|
||||
std::string result;
|
||||
ConsumeFd(std::move(output_fd), &result);
|
||||
ASSERT_MATCH(result, R"(Abort message: 'stack corruption detected \(-fstack-protector\)')");
|
||||
ASSERT_MATCH(result, R"(Abort message: 'abort message goes here')");
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, intercept_timeout) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
StartCrasher("abort");
|
||||
StartProcess([]() {
|
||||
abort();
|
||||
});
|
||||
StartIntercept(&output_fd);
|
||||
|
||||
// Don't let crasher finish until we timeout.
|
||||
|
@ -338,7 +342,9 @@ TEST_F(CrasherTest, wait_for_gdb) {
|
|||
}
|
||||
sleep(1);
|
||||
|
||||
StartCrasher("abort");
|
||||
StartProcess([]() {
|
||||
abort();
|
||||
});
|
||||
FinishCrasher();
|
||||
|
||||
int status;
|
||||
|
@ -357,7 +363,9 @@ TEST_F(CrasherTest, wait_for_gdb_signal) {
|
|||
FAIL() << "failed to enable wait_for_gdb";
|
||||
}
|
||||
|
||||
StartCrasher("abort");
|
||||
StartProcess([]() {
|
||||
abort();
|
||||
});
|
||||
ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
|
||||
AssertDeath(SIGSEGV);
|
||||
}
|
||||
|
@ -366,7 +374,10 @@ TEST_F(CrasherTest, backtrace) {
|
|||
std::string result;
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
StartCrasher("abort");
|
||||
|
||||
StartProcess([]() {
|
||||
abort();
|
||||
});
|
||||
StartIntercept(&output_fd);
|
||||
|
||||
std::this_thread::sleep_for(500ms);
|
||||
|
@ -392,20 +403,78 @@ TEST_F(CrasherTest, backtrace) {
|
|||
}
|
||||
|
||||
TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
StartProcess([]() {
|
||||
prctl(PR_SET_DUMPABLE, 0);
|
||||
volatile char* null = static_cast<char*>(nullptr);
|
||||
*null = '\0';
|
||||
abort();
|
||||
});
|
||||
AssertDeath(SIGSEGV);
|
||||
|
||||
StartIntercept(&output_fd);
|
||||
FinishCrasher();
|
||||
AssertDeath(SIGABRT);
|
||||
FinishIntercept(&intercept_result);
|
||||
|
||||
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
|
||||
|
||||
std::string result;
|
||||
ConsumeFd(std::move(output_fd), &result);
|
||||
ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, PR_SET_DUMPABLE_0_raise) {
|
||||
TEST_F(CrasherTest, capabilities) {
|
||||
ASSERT_EQ(0U, getuid()) << "capability test requires root";
|
||||
|
||||
StartProcess([]() {
|
||||
prctl(PR_SET_DUMPABLE, 0);
|
||||
raise(SIGUSR1);
|
||||
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) {
|
||||
err(1, "failed to set PR_SET_KEEPCAPS");
|
||||
}
|
||||
|
||||
if (setresuid(1, 1, 1) != 0) {
|
||||
err(1, "setresuid failed");
|
||||
}
|
||||
|
||||
__user_cap_header_struct capheader;
|
||||
__user_cap_data_struct capdata[2];
|
||||
memset(&capheader, 0, sizeof(capheader));
|
||||
memset(&capdata, 0, sizeof(capdata));
|
||||
|
||||
capheader.version = _LINUX_CAPABILITY_VERSION_3;
|
||||
capheader.pid = 0;
|
||||
|
||||
// Turn on every third capability.
|
||||
static_assert(CAP_LAST_CAP > 33, "CAP_LAST_CAP <= 32");
|
||||
for (int i = 0; i < CAP_LAST_CAP; i += 3) {
|
||||
capdata[CAP_TO_INDEX(i)].permitted |= CAP_TO_MASK(i);
|
||||
capdata[CAP_TO_INDEX(i)].effective |= CAP_TO_MASK(i);
|
||||
}
|
||||
|
||||
// Make sure CAP_SYS_PTRACE is off.
|
||||
capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].permitted &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));
|
||||
capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].effective &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));
|
||||
|
||||
if (capset(&capheader, &capdata[0]) != 0) {
|
||||
err(1, "capset failed");
|
||||
}
|
||||
|
||||
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) != 0) {
|
||||
err(1, "failed to drop ambient capabilities");
|
||||
}
|
||||
|
||||
raise(SIGSYS);
|
||||
});
|
||||
AssertDeath(SIGUSR1);
|
||||
|
||||
unique_fd output_fd;
|
||||
StartIntercept(&output_fd);
|
||||
FinishCrasher();
|
||||
AssertDeath(SIGSYS);
|
||||
|
||||
std::string result;
|
||||
int intercept_result;
|
||||
FinishIntercept(&intercept_result);
|
||||
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
|
||||
ConsumeFd(std::move(output_fd), &result);
|
||||
ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
|
||||
}
|
||||
|
||||
TEST(crash_dump, zombie) {
|
||||
|
|
Loading…
Reference in New Issue