diff options
Diffstat (limited to 'debuggerd/debuggerd_test.cpp')
| -rw-r--r-- | debuggerd/debuggerd_test.cpp | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp index 9d7658eb03..108787e4ab 100644 --- a/debuggerd/debuggerd_test.cpp +++ b/debuggerd/debuggerd_test.cpp @@ -18,6 +18,7 @@ #include <fcntl.h> #include <stdlib.h> #include <sys/capability.h> +#include <sys/mman.h> #include <sys/prctl.h> #include <sys/ptrace.h> #include <sys/resource.h> @@ -172,6 +173,8 @@ class CrasherTest : public ::testing::Test { void StartCrasher(const std::string& crash_type); void FinishCrasher(); void AssertDeath(int signo); + + static void Trap(void* ptr); }; CrasherTest::CrasherTest() { @@ -334,6 +337,48 @@ TEST_F(CrasherTest, tagged_fault_addr) { R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr (0x100000000000dead|0xdead))"); } +// Marked as weak to prevent the compiler from removing the malloc in the caller. In theory, the +// compiler could still clobber the argument register before trapping, but that's unlikely. +__attribute__((weak)) void CrasherTest::Trap(void* ptr ATTRIBUTE_UNUSED) { + __builtin_trap(); +} + +TEST_F(CrasherTest, heap_addr_in_register) { +#if defined(__i386__) + GTEST_SKIP() << "architecture does not pass arguments in registers"; +#endif + int intercept_result; + unique_fd output_fd; + StartProcess([]() { + // Crash with a heap pointer in the first argument register. + Trap(malloc(1)); + }); + + StartIntercept(&output_fd); + FinishCrasher(); + int status; + ASSERT_EQ(crasher_pid, TIMEOUT(30, waitpid(crasher_pid, &status, 0))); + ASSERT_TRUE(WIFSIGNALED(status)) << "crasher didn't terminate via a signal"; + // Don't test the signal number because different architectures use different signals for + // __builtin_trap(). + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + +#if defined(__aarch64__) + ASSERT_MATCH(result, "memory near x0"); +#elif defined(__arm__) + ASSERT_MATCH(result, "memory near r0"); +#elif defined(__x86_64__) + ASSERT_MATCH(result, "memory near rdi"); +#else + ASSERT_TRUE(false) << "unsupported architecture"; +#endif +} + #if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE) static void SetTagCheckingLevelSync() { int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); @@ -512,6 +557,55 @@ TEST_F(CrasherTest, mte_multiple_causes) { #endif } +#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE) +static uintptr_t CreateTagMapping() { + uintptr_t mapping = + reinterpret_cast<uintptr_t>(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + if (reinterpret_cast<void*>(mapping) == MAP_FAILED) { + return 0; + } + __asm__ __volatile__(".arch_extension mte; stg %0, [%0]" + : + : "r"(mapping + (1ULL << 56)) + : "memory"); + return mapping; +} +#endif + +TEST_F(CrasherTest, mte_tag_dump) { +#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE) + if (!mte_supported()) { + GTEST_SKIP() << "Requires MTE"; + } + + int intercept_result; + unique_fd output_fd; + StartProcess([&]() { + SetTagCheckingLevelSync(); + Trap(reinterpret_cast<void *>(CreateTagMapping())); + }); + + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGTRAP); + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + + ASSERT_MATCH(result, R"(memory near x0: +.* +.* + 01.............0 0000000000000000 0000000000000000 ................ + 00.............0)"); +#else + GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE"; +#endif +} + TEST_F(CrasherTest, LD_PRELOAD) { int intercept_result; unique_fd output_fd; |
