Loading debuggerd/debuggerd_test.cpp +211 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <dlfcn.h> #include <err.h> #include <fcntl.h> #include <linux/prctl.h> #include <malloc.h> #include <stdlib.h> #include <sys/capability.h> Loading @@ -31,6 +32,7 @@ #include <chrono> #include <regex> #include <set> #include <string> #include <thread> Loading @@ -54,6 +56,9 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> #include <unwindstack/Elf.h> #include <unwindstack/Memory.h> #include <libminijail.h> #include <scoped_minijail.h> Loading Loading @@ -2227,3 +2232,209 @@ TEST_F(CrasherTest, verify_dex_pc_with_function_name) { // Now verify that the dex_pc frame includes a proper function name. ASSERT_MATCH(result, R"( \[anon:dex\] \(Main\.\<init\>\+2)"); } static std::string format_map_pointer(uintptr_t ptr) { #if defined(__LP64__) return android::base::StringPrintf("%08x'%08x", static_cast<uint32_t>(ptr >> 32), static_cast<uint32_t>(ptr & 0xffffffff)); #else return android::base::StringPrintf("%08x", ptr); #endif } // Verify that map data is properly formatted. TEST_F(CrasherTest, verify_map_format) { // Create multiple maps to make sure that the map data is formatted properly. void* none_map = mmap(nullptr, getpagesize(), 0, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, none_map); void* r_map = mmap(nullptr, getpagesize(), PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, r_map); void* w_map = mmap(nullptr, getpagesize(), PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, w_map); void* x_map = mmap(nullptr, getpagesize(), PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, x_map); TemporaryFile tf; ASSERT_EQ(0x2000, lseek(tf.fd, 0x2000, SEEK_SET)); char c = 'f'; ASSERT_EQ(1, write(tf.fd, &c, 1)); ASSERT_EQ(0x5000, lseek(tf.fd, 0x5000, SEEK_SET)); ASSERT_EQ(1, write(tf.fd, &c, 1)); ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)); void* file_map = mmap(nullptr, 0x3001, PROT_READ, MAP_PRIVATE, tf.fd, 0x2000); ASSERT_NE(MAP_FAILED, file_map); StartProcess([]() { abort(); }); ASSERT_EQ(0, munmap(none_map, getpagesize())); ASSERT_EQ(0, munmap(r_map, getpagesize())); ASSERT_EQ(0, munmap(w_map, getpagesize())); ASSERT_EQ(0, munmap(x_map, getpagesize())); ASSERT_EQ(0, munmap(file_map, 0x3001)); unique_fd output_fd; StartIntercept(&output_fd); FinishCrasher(); AssertDeath(SIGABRT); int intercept_result; FinishIntercept(&intercept_result); ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; std::string result; ConsumeFd(std::move(output_fd), &result); std::string match_str; // Verify none. match_str = android::base::StringPrintf( " %s-%s --- 0 1000\\n", format_map_pointer(reinterpret_cast<uintptr_t>(none_map)).c_str(), format_map_pointer(reinterpret_cast<uintptr_t>(none_map) + getpagesize() - 1).c_str()); ASSERT_MATCH(result, match_str); // Verify read-only. match_str = android::base::StringPrintf( " %s-%s r-- 0 1000\\n", format_map_pointer(reinterpret_cast<uintptr_t>(r_map)).c_str(), format_map_pointer(reinterpret_cast<uintptr_t>(r_map) + getpagesize() - 1).c_str()); ASSERT_MATCH(result, match_str); // Verify write-only. match_str = android::base::StringPrintf( " %s-%s -w- 0 1000\\n", format_map_pointer(reinterpret_cast<uintptr_t>(w_map)).c_str(), format_map_pointer(reinterpret_cast<uintptr_t>(w_map) + getpagesize() - 1).c_str()); ASSERT_MATCH(result, match_str); // Verify exec-only. match_str = android::base::StringPrintf( " %s-%s --x 0 1000\\n", format_map_pointer(reinterpret_cast<uintptr_t>(x_map)).c_str(), format_map_pointer(reinterpret_cast<uintptr_t>(x_map) + getpagesize() - 1).c_str()); ASSERT_MATCH(result, match_str); // Verify file map with non-zero offset and a name. match_str = android::base::StringPrintf( " %s-%s r-- 2000 4000 %s\\n", format_map_pointer(reinterpret_cast<uintptr_t>(file_map)).c_str(), format_map_pointer(reinterpret_cast<uintptr_t>(file_map) + 0x3fff).c_str(), tf.path); ASSERT_MATCH(result, match_str); } // Verify that the tombstone map data is correct. TEST_F(CrasherTest, verify_header) { StartProcess([]() { abort(); }); unique_fd output_fd; StartIntercept(&output_fd); FinishCrasher(); AssertDeath(SIGABRT); int intercept_result; FinishIntercept(&intercept_result); ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; std::string result; ConsumeFd(std::move(output_fd), &result); std::string match_str = android::base::StringPrintf( "Build fingerprint: '%s'\\nRevision: '%s'\\n", android::base::GetProperty("ro.build.fingerprint", "unknown").c_str(), android::base::GetProperty("ro.revision", "unknown").c_str()); match_str += android::base::StringPrintf("ABI: '%s'\n", ABI_STRING); ASSERT_MATCH(result, match_str); } // Verify that the thread header is formatted properly. TEST_F(CrasherTest, verify_thread_header) { void* shared_map = mmap(nullptr, sizeof(pid_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); ASSERT_NE(MAP_FAILED, shared_map); memset(shared_map, 0, sizeof(pid_t)); StartProcess([&shared_map]() { std::atomic_bool tid_written; std::thread thread([&tid_written, &shared_map]() { pid_t tid = gettid(); memcpy(shared_map, &tid, sizeof(pid_t)); tid_written = true; volatile bool done = false; while (!done) ; }); thread.detach(); while (!tid_written.load(std::memory_order_acquire)) ; abort(); }); pid_t primary_pid = crasher_pid; unique_fd output_fd; StartIntercept(&output_fd); FinishCrasher(); AssertDeath(SIGABRT); int intercept_result; FinishIntercept(&intercept_result); ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; // Read the tid data out. pid_t tid; memcpy(&tid, shared_map, sizeof(pid_t)); ASSERT_NE(0, tid); ASSERT_EQ(0, munmap(shared_map, sizeof(pid_t))); std::string result; ConsumeFd(std::move(output_fd), &result); // Verify that there are two headers, one where the tid is "primary_pid" // and the other where the tid is "tid". std::string match_str = android::base::StringPrintf("pid: %d, tid: %d, name: .* >>> .* <<<\\n", primary_pid, primary_pid); ASSERT_MATCH(result, match_str); match_str = android::base::StringPrintf("pid: %d, tid: %d, name: .* >>> .* <<<\\n", primary_pid, tid); ASSERT_MATCH(result, match_str); } // Verify that there is a BuildID present in the map section and set properly. TEST_F(CrasherTest, verify_build_id) { StartProcess([]() { abort(); }); unique_fd output_fd; StartIntercept(&output_fd); FinishCrasher(); AssertDeath(SIGABRT); int intercept_result; FinishIntercept(&intercept_result); ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; std::string result; ConsumeFd(std::move(output_fd), &result); // Find every /system or /apex lib and verify the BuildID is displayed // properly. bool found_valid_elf = false; std::smatch match; std::regex build_id_regex(R"( ((/system/|/apex/)\S+) \(BuildId: ([^\)]+)\))"); for (std::string prev_file; std::regex_search(result, match, build_id_regex); result = match.suffix()) { if (prev_file == match[1]) { // Already checked this file. continue; } prev_file = match[1]; unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(prev_file, 0).release()); if (!elf.Init() || !elf.valid()) { // Skipping invalid elf files. continue; } ASSERT_EQ(match[3], elf.GetPrintableBuildID()); found_valid_elf = true; } ASSERT_TRUE(found_valid_elf) << "Did not find any elf files with valid BuildIDs to check."; } debuggerd/libdebuggerd/test/tombstone_test.cpp +0 −281 Original line number Diff line number Diff line Loading @@ -32,9 +32,6 @@ #include "host_signal_fixup.h" #include "log_fake.h" // Include tombstone.cpp to define log_tag before GWP-ASan includes log. #include "tombstone.cpp" #include "gwp_asan.cpp" using ::testing::MatchesRegex; Loading Loading @@ -82,283 +79,6 @@ class TombstoneTest : public ::testing::Test { std::string amfd_data_; }; TEST_F(TombstoneTest, single_map) { #if defined(__LP64__) unwinder_mock_->MockAddMap(0x123456789abcd000UL, 0x123456789abdf000UL, 0, 0, "", 0); #else unwinder_mock_->MockAddMap(0x1234000, 0x1235000, 0, 0, "", 0); #endif dump_all_maps(&log_, unwinder_mock_.get(), 0); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = \ "\nmemory map (1 entry):\n" #if defined(__LP64__) " 12345678'9abcd000-12345678'9abdefff --- 0 12000\n"; #else " 01234000-01234fff --- 0 1000\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, single_map_elf_build_id) { uint64_t build_id_offset; #if defined(__LP64__) build_id_offset = 0x123456789abcd000UL; unwinder_mock_->MockAddMap(build_id_offset, 0x123456789abdf000UL, 0, PROT_READ, "/system/lib/libfake.so", 0); #else build_id_offset = 0x1234000; unwinder_mock_->MockAddMap(0x1234000, 0x1235000, 0, PROT_READ, "/system/lib/libfake.so", 0); #endif unwinder_mock_->MockSetBuildID( build_id_offset, std::string{static_cast<char>(0xab), static_cast<char>(0xcd), static_cast<char>(0xef), static_cast<char>(0x12), static_cast<char>(0x34), static_cast<char>(0x56), static_cast<char>(0x78), static_cast<char>(0x90), static_cast<char>(0xab), static_cast<char>(0xcd), static_cast<char>(0xef), static_cast<char>(0x12), static_cast<char>(0x34), static_cast<char>(0x56), static_cast<char>(0x78), static_cast<char>(0x90)}); dump_all_maps(&log_, unwinder_mock_.get(), 0); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = \ "\nmemory map (1 entry):\n" #if defined(__LP64__) " 12345678'9abcd000-12345678'9abdefff r-- 0 12000 /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n"; #else " 01234000-01234fff r-- 0 1000 /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, multiple_maps) { unwinder_mock_->MockAddMap(0xa234000, 0xa235000, 0, 0, "", 0); unwinder_mock_->MockAddMap(0xa334000, 0xa335000, 0xf000, PROT_READ, "", 0); unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000); unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000); unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/system/lib/fake.so", 0); dump_all_maps(&log_, unwinder_mock_.get(), 0); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = "\nmemory map (5 entries):\n" #if defined(__LP64__) " 00000000'0a234000-00000000'0a234fff --- 0 1000\n" " 00000000'0a334000-00000000'0a334fff r-- f000 1000\n" " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; #else " 0a234000-0a234fff --- 0 1000\n" " 0a334000-0a334fff r-- f000 1000\n" " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, multiple_maps_fault_address_before) { unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000); unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000); unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/system/lib/fake.so", 0); dump_all_maps(&log_, unwinder_mock_.get(), 0x1000); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = "\nmemory map (3 entries):\n" #if defined(__LP64__) "--->Fault address falls at 00000000'00001000 before any mapped regions\n" " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; #else "--->Fault address falls at 00001000 before any mapped regions\n" " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, multiple_maps_fault_address_between) { unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000); unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000); unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/system/lib/fake.so", 0); dump_all_maps(&log_, unwinder_mock_.get(), 0xa533000); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = "\nmemory map (3 entries): (fault address prefixed with --->)\n" #if defined(__LP64__) " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" "--->Fault address falls at 00000000'0a533000 between mapped regions\n" " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; #else " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" "--->Fault address falls at 0a533000 between mapped regions\n" " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) { unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000); unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000); unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/system/lib/fake.so", 0); dump_all_maps(&log_, unwinder_mock_.get(), 0xa534040); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = "\nmemory map (3 entries): (fault address prefixed with --->)\n" #if defined(__LP64__) " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" "--->00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; #else " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" "--->0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, multiple_maps_fault_address_after) { unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000); unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000); unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/system/lib/fake.so", 0); #if defined(__LP64__) uint64_t addr = 0x12345a534040UL; #else uint64_t addr = 0xf534040UL; #endif dump_all_maps(&log_, unwinder_mock_.get(), addr); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = "\nmemory map (3 entries): (fault address prefixed with --->)\n" #if defined(__LP64__) " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n" "--->Fault address falls at 00001234'5a534040 after any mapped regions\n"; #else " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n" "--->Fault address falls at 0f534040 after any mapped regions\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, dump_log_file_error) { log_.should_retrieve_logcat = true; dump_log_file(&log_, 123, "/fake/filename", 10); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); ASSERT_STREQ("", tombstone_contents.c_str()); ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("6 DEBUG Unable to open /fake/filename: Permission denied\n\n", getFakeLogPrint().c_str()); ASSERT_STREQ("", amfd_data_.c_str()); } TEST_F(TombstoneTest, dump_header_info) { dump_header_info(&log_); std::string expected = android::base::StringPrintf( "Build fingerprint: '%s'\nRevision: '%s'\n", android::base::GetProperty("ro.build.fingerprint", "unknown").c_str(), android::base::GetProperty("ro.revision", "unknown").c_str()); expected += android::base::StringPrintf("ABI: '%s'\n", ABI_STRING); ASSERT_STREQ(expected.c_str(), amfd_data_.c_str()); } TEST_F(TombstoneTest, dump_thread_info_uid) { std::vector<std::string> cmdline = {"some_process"}; dump_thread_info( &log_, ThreadInfo{ .uid = 1, .tid = 3, .thread_name = "some_thread", .pid = 2, .command_line = cmdline}); std::string expected = "pid: 2, tid: 3, name: some_thread >>> some_process <<<\nuid: 1\n"; ASSERT_STREQ(expected.c_str(), amfd_data_.c_str()); } class GwpAsanCrashDataTest : public GwpAsanCrashData { public: GwpAsanCrashDataTest( Loading Loading @@ -483,4 +203,3 @@ TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_outside) { "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 33 bytes right of a 32-byte " "allocation at 0x[a-fA-F0-9]+\n")); } debuggerd/libdebuggerd/tombstone.cpp +5 −571 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
debuggerd/debuggerd_test.cpp +211 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <dlfcn.h> #include <err.h> #include <fcntl.h> #include <linux/prctl.h> #include <malloc.h> #include <stdlib.h> #include <sys/capability.h> Loading @@ -31,6 +32,7 @@ #include <chrono> #include <regex> #include <set> #include <string> #include <thread> Loading @@ -54,6 +56,9 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> #include <unwindstack/Elf.h> #include <unwindstack/Memory.h> #include <libminijail.h> #include <scoped_minijail.h> Loading Loading @@ -2227,3 +2232,209 @@ TEST_F(CrasherTest, verify_dex_pc_with_function_name) { // Now verify that the dex_pc frame includes a proper function name. ASSERT_MATCH(result, R"( \[anon:dex\] \(Main\.\<init\>\+2)"); } static std::string format_map_pointer(uintptr_t ptr) { #if defined(__LP64__) return android::base::StringPrintf("%08x'%08x", static_cast<uint32_t>(ptr >> 32), static_cast<uint32_t>(ptr & 0xffffffff)); #else return android::base::StringPrintf("%08x", ptr); #endif } // Verify that map data is properly formatted. TEST_F(CrasherTest, verify_map_format) { // Create multiple maps to make sure that the map data is formatted properly. void* none_map = mmap(nullptr, getpagesize(), 0, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, none_map); void* r_map = mmap(nullptr, getpagesize(), PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, r_map); void* w_map = mmap(nullptr, getpagesize(), PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, w_map); void* x_map = mmap(nullptr, getpagesize(), PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, x_map); TemporaryFile tf; ASSERT_EQ(0x2000, lseek(tf.fd, 0x2000, SEEK_SET)); char c = 'f'; ASSERT_EQ(1, write(tf.fd, &c, 1)); ASSERT_EQ(0x5000, lseek(tf.fd, 0x5000, SEEK_SET)); ASSERT_EQ(1, write(tf.fd, &c, 1)); ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)); void* file_map = mmap(nullptr, 0x3001, PROT_READ, MAP_PRIVATE, tf.fd, 0x2000); ASSERT_NE(MAP_FAILED, file_map); StartProcess([]() { abort(); }); ASSERT_EQ(0, munmap(none_map, getpagesize())); ASSERT_EQ(0, munmap(r_map, getpagesize())); ASSERT_EQ(0, munmap(w_map, getpagesize())); ASSERT_EQ(0, munmap(x_map, getpagesize())); ASSERT_EQ(0, munmap(file_map, 0x3001)); unique_fd output_fd; StartIntercept(&output_fd); FinishCrasher(); AssertDeath(SIGABRT); int intercept_result; FinishIntercept(&intercept_result); ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; std::string result; ConsumeFd(std::move(output_fd), &result); std::string match_str; // Verify none. match_str = android::base::StringPrintf( " %s-%s --- 0 1000\\n", format_map_pointer(reinterpret_cast<uintptr_t>(none_map)).c_str(), format_map_pointer(reinterpret_cast<uintptr_t>(none_map) + getpagesize() - 1).c_str()); ASSERT_MATCH(result, match_str); // Verify read-only. match_str = android::base::StringPrintf( " %s-%s r-- 0 1000\\n", format_map_pointer(reinterpret_cast<uintptr_t>(r_map)).c_str(), format_map_pointer(reinterpret_cast<uintptr_t>(r_map) + getpagesize() - 1).c_str()); ASSERT_MATCH(result, match_str); // Verify write-only. match_str = android::base::StringPrintf( " %s-%s -w- 0 1000\\n", format_map_pointer(reinterpret_cast<uintptr_t>(w_map)).c_str(), format_map_pointer(reinterpret_cast<uintptr_t>(w_map) + getpagesize() - 1).c_str()); ASSERT_MATCH(result, match_str); // Verify exec-only. match_str = android::base::StringPrintf( " %s-%s --x 0 1000\\n", format_map_pointer(reinterpret_cast<uintptr_t>(x_map)).c_str(), format_map_pointer(reinterpret_cast<uintptr_t>(x_map) + getpagesize() - 1).c_str()); ASSERT_MATCH(result, match_str); // Verify file map with non-zero offset and a name. match_str = android::base::StringPrintf( " %s-%s r-- 2000 4000 %s\\n", format_map_pointer(reinterpret_cast<uintptr_t>(file_map)).c_str(), format_map_pointer(reinterpret_cast<uintptr_t>(file_map) + 0x3fff).c_str(), tf.path); ASSERT_MATCH(result, match_str); } // Verify that the tombstone map data is correct. TEST_F(CrasherTest, verify_header) { StartProcess([]() { abort(); }); unique_fd output_fd; StartIntercept(&output_fd); FinishCrasher(); AssertDeath(SIGABRT); int intercept_result; FinishIntercept(&intercept_result); ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; std::string result; ConsumeFd(std::move(output_fd), &result); std::string match_str = android::base::StringPrintf( "Build fingerprint: '%s'\\nRevision: '%s'\\n", android::base::GetProperty("ro.build.fingerprint", "unknown").c_str(), android::base::GetProperty("ro.revision", "unknown").c_str()); match_str += android::base::StringPrintf("ABI: '%s'\n", ABI_STRING); ASSERT_MATCH(result, match_str); } // Verify that the thread header is formatted properly. TEST_F(CrasherTest, verify_thread_header) { void* shared_map = mmap(nullptr, sizeof(pid_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); ASSERT_NE(MAP_FAILED, shared_map); memset(shared_map, 0, sizeof(pid_t)); StartProcess([&shared_map]() { std::atomic_bool tid_written; std::thread thread([&tid_written, &shared_map]() { pid_t tid = gettid(); memcpy(shared_map, &tid, sizeof(pid_t)); tid_written = true; volatile bool done = false; while (!done) ; }); thread.detach(); while (!tid_written.load(std::memory_order_acquire)) ; abort(); }); pid_t primary_pid = crasher_pid; unique_fd output_fd; StartIntercept(&output_fd); FinishCrasher(); AssertDeath(SIGABRT); int intercept_result; FinishIntercept(&intercept_result); ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; // Read the tid data out. pid_t tid; memcpy(&tid, shared_map, sizeof(pid_t)); ASSERT_NE(0, tid); ASSERT_EQ(0, munmap(shared_map, sizeof(pid_t))); std::string result; ConsumeFd(std::move(output_fd), &result); // Verify that there are two headers, one where the tid is "primary_pid" // and the other where the tid is "tid". std::string match_str = android::base::StringPrintf("pid: %d, tid: %d, name: .* >>> .* <<<\\n", primary_pid, primary_pid); ASSERT_MATCH(result, match_str); match_str = android::base::StringPrintf("pid: %d, tid: %d, name: .* >>> .* <<<\\n", primary_pid, tid); ASSERT_MATCH(result, match_str); } // Verify that there is a BuildID present in the map section and set properly. TEST_F(CrasherTest, verify_build_id) { StartProcess([]() { abort(); }); unique_fd output_fd; StartIntercept(&output_fd); FinishCrasher(); AssertDeath(SIGABRT); int intercept_result; FinishIntercept(&intercept_result); ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; std::string result; ConsumeFd(std::move(output_fd), &result); // Find every /system or /apex lib and verify the BuildID is displayed // properly. bool found_valid_elf = false; std::smatch match; std::regex build_id_regex(R"( ((/system/|/apex/)\S+) \(BuildId: ([^\)]+)\))"); for (std::string prev_file; std::regex_search(result, match, build_id_regex); result = match.suffix()) { if (prev_file == match[1]) { // Already checked this file. continue; } prev_file = match[1]; unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(prev_file, 0).release()); if (!elf.Init() || !elf.valid()) { // Skipping invalid elf files. continue; } ASSERT_EQ(match[3], elf.GetPrintableBuildID()); found_valid_elf = true; } ASSERT_TRUE(found_valid_elf) << "Did not find any elf files with valid BuildIDs to check."; }
debuggerd/libdebuggerd/test/tombstone_test.cpp +0 −281 Original line number Diff line number Diff line Loading @@ -32,9 +32,6 @@ #include "host_signal_fixup.h" #include "log_fake.h" // Include tombstone.cpp to define log_tag before GWP-ASan includes log. #include "tombstone.cpp" #include "gwp_asan.cpp" using ::testing::MatchesRegex; Loading Loading @@ -82,283 +79,6 @@ class TombstoneTest : public ::testing::Test { std::string amfd_data_; }; TEST_F(TombstoneTest, single_map) { #if defined(__LP64__) unwinder_mock_->MockAddMap(0x123456789abcd000UL, 0x123456789abdf000UL, 0, 0, "", 0); #else unwinder_mock_->MockAddMap(0x1234000, 0x1235000, 0, 0, "", 0); #endif dump_all_maps(&log_, unwinder_mock_.get(), 0); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = \ "\nmemory map (1 entry):\n" #if defined(__LP64__) " 12345678'9abcd000-12345678'9abdefff --- 0 12000\n"; #else " 01234000-01234fff --- 0 1000\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, single_map_elf_build_id) { uint64_t build_id_offset; #if defined(__LP64__) build_id_offset = 0x123456789abcd000UL; unwinder_mock_->MockAddMap(build_id_offset, 0x123456789abdf000UL, 0, PROT_READ, "/system/lib/libfake.so", 0); #else build_id_offset = 0x1234000; unwinder_mock_->MockAddMap(0x1234000, 0x1235000, 0, PROT_READ, "/system/lib/libfake.so", 0); #endif unwinder_mock_->MockSetBuildID( build_id_offset, std::string{static_cast<char>(0xab), static_cast<char>(0xcd), static_cast<char>(0xef), static_cast<char>(0x12), static_cast<char>(0x34), static_cast<char>(0x56), static_cast<char>(0x78), static_cast<char>(0x90), static_cast<char>(0xab), static_cast<char>(0xcd), static_cast<char>(0xef), static_cast<char>(0x12), static_cast<char>(0x34), static_cast<char>(0x56), static_cast<char>(0x78), static_cast<char>(0x90)}); dump_all_maps(&log_, unwinder_mock_.get(), 0); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = \ "\nmemory map (1 entry):\n" #if defined(__LP64__) " 12345678'9abcd000-12345678'9abdefff r-- 0 12000 /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n"; #else " 01234000-01234fff r-- 0 1000 /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, multiple_maps) { unwinder_mock_->MockAddMap(0xa234000, 0xa235000, 0, 0, "", 0); unwinder_mock_->MockAddMap(0xa334000, 0xa335000, 0xf000, PROT_READ, "", 0); unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000); unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000); unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/system/lib/fake.so", 0); dump_all_maps(&log_, unwinder_mock_.get(), 0); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = "\nmemory map (5 entries):\n" #if defined(__LP64__) " 00000000'0a234000-00000000'0a234fff --- 0 1000\n" " 00000000'0a334000-00000000'0a334fff r-- f000 1000\n" " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; #else " 0a234000-0a234fff --- 0 1000\n" " 0a334000-0a334fff r-- f000 1000\n" " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, multiple_maps_fault_address_before) { unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000); unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000); unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/system/lib/fake.so", 0); dump_all_maps(&log_, unwinder_mock_.get(), 0x1000); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = "\nmemory map (3 entries):\n" #if defined(__LP64__) "--->Fault address falls at 00000000'00001000 before any mapped regions\n" " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; #else "--->Fault address falls at 00001000 before any mapped regions\n" " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, multiple_maps_fault_address_between) { unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000); unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000); unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/system/lib/fake.so", 0); dump_all_maps(&log_, unwinder_mock_.get(), 0xa533000); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = "\nmemory map (3 entries): (fault address prefixed with --->)\n" #if defined(__LP64__) " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" "--->Fault address falls at 00000000'0a533000 between mapped regions\n" " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; #else " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" "--->Fault address falls at 0a533000 between mapped regions\n" " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) { unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000); unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000); unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/system/lib/fake.so", 0); dump_all_maps(&log_, unwinder_mock_.get(), 0xa534040); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = "\nmemory map (3 entries): (fault address prefixed with --->)\n" #if defined(__LP64__) " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" "--->00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n"; #else " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" "--->0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, multiple_maps_fault_address_after) { unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000); unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000); unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/system/lib/fake.so", 0); #if defined(__LP64__) uint64_t addr = 0x12345a534040UL; #else uint64_t addr = 0xf534040UL; #endif dump_all_maps(&log_, unwinder_mock_.get(), addr); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); const char* expected_dump = "\nmemory map (3 entries): (fault address prefixed with --->)\n" #if defined(__LP64__) " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n" " 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n" " 00000000'0a634000-00000000'0a634fff rwx 0 1000 /system/lib/fake.so\n" "--->Fault address falls at 00001234'5a534040 after any mapped regions\n"; #else " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n" " 0a534000-0a534fff --x 3000 1000 (load bias 0x2000)\n" " 0a634000-0a634fff rwx 0 1000 /system/lib/fake.so\n" "--->Fault address falls at 0f534040 after any mapped regions\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); ASSERT_STREQ("", amfd_data_.c_str()); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); } TEST_F(TombstoneTest, dump_log_file_error) { log_.should_retrieve_logcat = true; dump_log_file(&log_, 123, "/fake/filename", 10); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); ASSERT_STREQ("", tombstone_contents.c_str()); ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("6 DEBUG Unable to open /fake/filename: Permission denied\n\n", getFakeLogPrint().c_str()); ASSERT_STREQ("", amfd_data_.c_str()); } TEST_F(TombstoneTest, dump_header_info) { dump_header_info(&log_); std::string expected = android::base::StringPrintf( "Build fingerprint: '%s'\nRevision: '%s'\n", android::base::GetProperty("ro.build.fingerprint", "unknown").c_str(), android::base::GetProperty("ro.revision", "unknown").c_str()); expected += android::base::StringPrintf("ABI: '%s'\n", ABI_STRING); ASSERT_STREQ(expected.c_str(), amfd_data_.c_str()); } TEST_F(TombstoneTest, dump_thread_info_uid) { std::vector<std::string> cmdline = {"some_process"}; dump_thread_info( &log_, ThreadInfo{ .uid = 1, .tid = 3, .thread_name = "some_thread", .pid = 2, .command_line = cmdline}); std::string expected = "pid: 2, tid: 3, name: some_thread >>> some_process <<<\nuid: 1\n"; ASSERT_STREQ(expected.c_str(), amfd_data_.c_str()); } class GwpAsanCrashDataTest : public GwpAsanCrashData { public: GwpAsanCrashDataTest( Loading Loading @@ -483,4 +203,3 @@ TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_outside) { "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 33 bytes right of a 32-byte " "allocation at 0x[a-fA-F0-9]+\n")); }
debuggerd/libdebuggerd/tombstone.cpp +5 −571 File changed.Preview size limit exceeded, changes collapsed. Show changes