diff options
-rw-r--r-- | debuggerd/debuggerd_test.cpp | 17 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/gwp_asan.cpp | 74 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h | 5 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/include/libdebuggerd/scudo.h | 7 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h | 8 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/include/libdebuggerd/utility.h | 5 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/scudo.cpp | 65 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/test/dump_memory_test.cpp | 76 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/tombstone_proto.cpp | 209 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/tombstone_proto_to_text.cpp | 67 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/utility.cpp | 62 | ||||
-rw-r--r-- | debuggerd/proto/tombstone.proto | 52 |
12 files changed, 452 insertions, 195 deletions
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp index ab95768de..dcb5395d4 100644 --- a/debuggerd/debuggerd_test.cpp +++ b/debuggerd/debuggerd_test.cpp @@ -423,12 +423,11 @@ TEST_P(SizeParamCrasherTest, mte_uaf) { ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))"); ASSERT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 0 bytes into a )" + - std::to_string(GetParam()) + R"(-byte allocation.* - -allocated by thread .* - #00 pc)"); + std::to_string(GetParam()) + R"(-byte allocation)"); ASSERT_MATCH(result, R"(deallocated by thread .* #00 pc)"); + ASSERT_MATCH(result, R"(allocated by thread .* + #00 pc)"); #else GTEST_SKIP() << "Requires aarch64"; #endif @@ -460,9 +459,8 @@ TEST_P(SizeParamCrasherTest, mte_overflow) { ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))"); ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a )" + - std::to_string(GetParam()) + R"(-byte allocation.* - -allocated by thread .* + std::to_string(GetParam()) + R"(-byte allocation)"); + ASSERT_MATCH(result, R"(allocated by thread .* #00 pc)"); #else GTEST_SKIP() << "Requires aarch64"; @@ -495,9 +493,8 @@ TEST_P(SizeParamCrasherTest, mte_underflow) { ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))"); ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Underflow, 4 bytes left of a )" + - std::to_string(GetParam()) + R"(-byte allocation.* - -allocated by thread .* + std::to_string(GetParam()) + R"(-byte allocation)"); + ASSERT_MATCH(result, R"(allocated by thread .* #00 pc)"); #else GTEST_SKIP() << "Requires aarch64"; diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp index 9750fc4b0..6b8af7f54 100644 --- a/debuggerd/libdebuggerd/gwp_asan.cpp +++ b/debuggerd/libdebuggerd/gwp_asan.cpp @@ -15,6 +15,7 @@ */ #include "libdebuggerd/gwp_asan.h" +#include "libdebuggerd/tombstone.h" #include "libdebuggerd/utility.h" #include "gwp_asan/common.h" @@ -25,6 +26,8 @@ #include <unwindstack/Regs.h> #include <unwindstack/Unwinder.h> +#include "tombstone.pb.h" + // Retrieve GWP-ASan state from `state_addr` inside the process at // `process_memory`. Place the state into `*state`. static bool retrieve_gwp_asan_state(unwindstack::Memory* process_memory, uintptr_t state_addr, @@ -98,6 +101,67 @@ bool GwpAsanCrashData::CrashIsMine() const { return is_gwp_asan_responsible_; } +constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect; + +void GwpAsanCrashData::AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const { + if (!CrashIsMine()) { + ALOGE("Internal Error: AddCauseProtos() on a non-GWP-ASan crash."); + return; + } + + Cause* cause = tombstone->add_causes(); + MemoryError* memory_error = cause->mutable_memory_error(); + HeapObject* heap_object = memory_error->mutable_heap(); + + memory_error->set_tool(MemoryError_Tool_GWP_ASAN); + switch (error_) { + case gwp_asan::Error::USE_AFTER_FREE: + memory_error->set_type(MemoryError_Type_USE_AFTER_FREE); + break; + case gwp_asan::Error::DOUBLE_FREE: + memory_error->set_type(MemoryError_Type_DOUBLE_FREE); + break; + case gwp_asan::Error::INVALID_FREE: + memory_error->set_type(MemoryError_Type_INVALID_FREE); + break; + case gwp_asan::Error::BUFFER_OVERFLOW: + memory_error->set_type(MemoryError_Type_BUFFER_OVERFLOW); + break; + case gwp_asan::Error::BUFFER_UNDERFLOW: + memory_error->set_type(MemoryError_Type_BUFFER_UNDERFLOW); + break; + default: + memory_error->set_type(MemoryError_Type_UNKNOWN); + break; + } + + heap_object->set_address(__gwp_asan_get_allocation_address(responsible_allocation_)); + heap_object->set_size(__gwp_asan_get_allocation_size(responsible_allocation_)); + unwinder->SetDisplayBuildID(true); + + std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]); + + heap_object->set_allocation_tid(__gwp_asan_get_allocation_thread_id(responsible_allocation_)); + size_t num_frames = + __gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength); + for (size_t i = 0; i != num_frames; ++i) { + unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]); + BacktraceFrame* f = heap_object->add_allocation_backtrace(); + fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps()); + } + + heap_object->set_deallocation_tid(__gwp_asan_get_deallocation_thread_id(responsible_allocation_)); + num_frames = + __gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength); + for (size_t i = 0; i != num_frames; ++i) { + unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]); + BacktraceFrame* f = heap_object->add_deallocation_backtrace(); + fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps()); + } + + set_human_readable_cause(cause, crash_address_); +} + void GwpAsanCrashData::DumpCause(log_t* log) const { if (!CrashIsMine()) { ALOGE("Internal Error: DumpCause() on a non-GWP-ASan crash."); @@ -157,8 +221,6 @@ void GwpAsanCrashData::DumpCause(log_t* log) const { error_string_, diff, byte_suffix, location_str, alloc_size, alloc_address); } -constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect; - bool GwpAsanCrashData::HasDeallocationTrace() const { assert(CrashIsMine() && "HasDeallocationTrace(): Crash is not mine!"); if (!responsible_allocation_ || !__gwp_asan_is_deallocated(responsible_allocation_)) { @@ -171,7 +233,7 @@ void GwpAsanCrashData::DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* assert(HasDeallocationTrace() && "DumpDeallocationTrace(): No dealloc trace!"); uint64_t thread_id = __gwp_asan_get_deallocation_thread_id(responsible_allocation_); - std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]); + std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]); size_t num_frames = __gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength); @@ -183,7 +245,7 @@ void GwpAsanCrashData::DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder->SetDisplayBuildID(true); for (size_t i = 0; i < num_frames; ++i) { - unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames.get()[i]); + unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]); frame_data.num = i; _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str()); } @@ -198,7 +260,7 @@ void GwpAsanCrashData::DumpAllocationTrace(log_t* log, unwindstack::Unwinder* un assert(HasAllocationTrace() && "DumpAllocationTrace(): No dealloc trace!"); uint64_t thread_id = __gwp_asan_get_allocation_thread_id(responsible_allocation_); - std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]); + std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]); size_t num_frames = __gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength); @@ -210,7 +272,7 @@ void GwpAsanCrashData::DumpAllocationTrace(log_t* log, unwindstack::Unwinder* un unwinder->SetDisplayBuildID(true); for (size_t i = 0; i < num_frames; ++i) { - unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames.get()[i]); + unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]); frame_data.num = i; _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str()); } diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h index 6c8873341..f9c2481a9 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h @@ -26,6 +26,9 @@ #include "types.h" #include "utility.h" +class Cause; +class Tombstone; + class GwpAsanCrashData { public: GwpAsanCrashData() = delete; @@ -69,6 +72,8 @@ class GwpAsanCrashData { // HasAllocationTrace() returns true. void DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const; + void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const; + protected: // Is GWP-ASan responsible for this crash. bool is_gwp_asan_responsible_ = false; diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h index 4d00ecede..c3b95d608 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h @@ -23,6 +23,9 @@ #include "scudo/interface.h" +class Cause; +class Tombstone; + class ScudoCrashData { public: ScudoCrashData() = delete; @@ -32,6 +35,7 @@ class ScudoCrashData { bool CrashIsMine() const; void DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const; + void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const; private: scudo_error_info error_info_ = {}; @@ -39,4 +43,7 @@ class ScudoCrashData { void DumpReport(const scudo_error_report* report, log_t* log, unwindstack::Unwinder* unwinder) const; + + void FillInCause(Cause* cause, const scudo_error_report* report, + unwindstack::Unwinder* unwinder) const; }; diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h index bf2cbb3fb..2331f1e6e 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h @@ -31,9 +31,13 @@ #include "types.h" // Forward declarations +class BacktraceFrame; +class Cause; class Tombstone; namespace unwindstack { +struct FrameData; +class Maps; class Unwinder; } @@ -64,4 +68,8 @@ bool tombstone_proto_to_text( const Tombstone& tombstone, std::function<void(const std::string& line, bool should_log)> callback); +void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame, + unwindstack::Maps* maps); +void set_human_readable_cause(Cause* cause, uint64_t fault_addr); + #endif // _DEBUGGERD_TOMBSTONE_H diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h index d71b76f7c..c490fb1b9 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h @@ -81,7 +81,7 @@ class Memory; void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix); -ssize_t dump_memory(void* out, size_t len, size_t* start_offset, uint64_t* addr, +ssize_t dump_memory(void* out, size_t len, uint8_t* tags, size_t tags_len, uint64_t* addr, unwindstack::Memory* memory); void dump_memory(log_t* log, unwindstack::Memory* backtrace, uint64_t addr, const std::string&); @@ -93,4 +93,7 @@ void get_signal_sender(char* buf, size_t n, const siginfo_t*); const char* get_signame(const siginfo_t*); const char* get_sigcode(const siginfo_t*); +// Number of bytes per MTE granule. +constexpr size_t kTagGranuleSize = 16; + #endif // _DEBUGGERD_UTILITY_H diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp index 1c3437fde..f4690bac3 100644 --- a/debuggerd/libdebuggerd/scudo.cpp +++ b/debuggerd/libdebuggerd/scudo.cpp @@ -15,13 +15,16 @@ */ #include "libdebuggerd/scudo.h" -#include "libdebuggerd/gwp_asan.h" +#include "libdebuggerd/tombstone.h" #include "unwindstack/Memory.h" #include "unwindstack/Unwinder.h" +#include <android-base/macros.h> #include <bionic/macros.h> +#include "tombstone.pb.h" + std::unique_ptr<char[]> AllocAndReadFully(unwindstack::Memory* process_memory, uint64_t addr, size_t size) { auto buf = std::make_unique<char[]>(size); @@ -31,8 +34,6 @@ std::unique_ptr<char[]> AllocAndReadFully(unwindstack::Memory* process_memory, u return buf; } -static const uintptr_t kTagGranuleSize = 16; - ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory, const ProcessInfo& process_info) { if (!process_info.has_fault_address) { @@ -78,6 +79,58 @@ bool ScudoCrashData::CrashIsMine() const { return error_info_.reports[0].error_type != UNKNOWN; } +void ScudoCrashData::FillInCause(Cause* cause, const scudo_error_report* report, + unwindstack::Unwinder* unwinder) const { + MemoryError* memory_error = cause->mutable_memory_error(); + HeapObject* heap_object = memory_error->mutable_heap(); + + memory_error->set_tool(MemoryError_Tool_SCUDO); + switch (report->error_type) { + case USE_AFTER_FREE: + memory_error->set_type(MemoryError_Type_USE_AFTER_FREE); + break; + case BUFFER_OVERFLOW: + memory_error->set_type(MemoryError_Type_BUFFER_OVERFLOW); + break; + case BUFFER_UNDERFLOW: + memory_error->set_type(MemoryError_Type_BUFFER_UNDERFLOW); + break; + default: + memory_error->set_type(MemoryError_Type_UNKNOWN); + break; + } + + heap_object->set_address(report->allocation_address); + heap_object->set_size(report->allocation_size); + unwinder->SetDisplayBuildID(true); + + heap_object->set_allocation_tid(report->allocation_tid); + for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i]; ++i) { + unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]); + BacktraceFrame* f = heap_object->add_allocation_backtrace(); + fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps()); + } + + heap_object->set_deallocation_tid(report->deallocation_tid); + for (size_t i = 0; i < arraysize(report->deallocation_trace) && report->deallocation_trace[i]; + ++i) { + unwindstack::FrameData frame_data = + unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]); + BacktraceFrame* f = heap_object->add_deallocation_backtrace(); + fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps()); + } + + set_human_readable_cause(cause, untagged_fault_addr_); +} + +void ScudoCrashData::AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const { + size_t report_num = 0; + while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) && + error_info_.reports[report_num].error_type != UNKNOWN) { + FillInCause(tombstone->add_causes(), &error_info_.reports[report_num++], unwinder); + } +} + void ScudoCrashData::DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const { if (error_info_.reports[1].error_type != UNKNOWN) { _LOG(log, logtype::HEADER, @@ -140,7 +193,8 @@ void ScudoCrashData::DumpReport(const scudo_error_report* report, log_t* log, if (report->allocation_trace[0]) { _LOG(log, logtype::BACKTRACE, "\nallocated by thread %u:\n", report->allocation_tid); unwinder->SetDisplayBuildID(true); - for (size_t i = 0; i < 64 && report->allocation_trace[i]; ++i) { + for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i]; + ++i) { unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]); frame_data.num = i; @@ -151,7 +205,8 @@ void ScudoCrashData::DumpReport(const scudo_error_report* report, log_t* log, if (report->deallocation_trace[0]) { _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %u:\n", report->deallocation_tid); unwinder->SetDisplayBuildID(true); - for (size_t i = 0; i < 64 && report->deallocation_trace[i]; ++i) { + for (size_t i = 0; i < arraysize(report->deallocation_trace) && report->deallocation_trace[i]; + ++i) { unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]); frame_data.num = i; diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp index f16f578a4..5be145aad 100644 --- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp +++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp @@ -73,34 +73,14 @@ const char g_expected_partial_dump[] = \ " 0000000012345600 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n" " 0000000012345610 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n" " 0000000012345620 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n" -" 0000000012345630 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n" -" 0000000012345640 6766656463626160 ---------------- `abcdefg........\n" -" 0000000012345650 ---------------- ---------------- ................\n" -" 0000000012345660 ---------------- ---------------- ................\n" -" 0000000012345670 ---------------- ---------------- ................\n" -" 0000000012345680 ---------------- ---------------- ................\n" -" 0000000012345690 ---------------- ---------------- ................\n" -" 00000000123456a0 ---------------- ---------------- ................\n" -" 00000000123456b0 ---------------- ---------------- ................\n" -" 00000000123456c0 ---------------- ---------------- ................\n" -" 00000000123456d0 ---------------- ---------------- ................\n"; +" 0000000012345630 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"; #else " 123455e0 03020100 07060504 0b0a0908 0f0e0d0c ................\n" " 123455f0 13121110 17161514 1b1a1918 1f1e1d1c ................\n" " 12345600 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n" " 12345610 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n" " 12345620 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n" -" 12345630 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n" -" 12345640 63626160 67666564 -------- -------- `abcdefg........\n" -" 12345650 -------- -------- -------- -------- ................\n" -" 12345660 -------- -------- -------- -------- ................\n" -" 12345670 -------- -------- -------- -------- ................\n" -" 12345680 -------- -------- -------- -------- ................\n" -" 12345690 -------- -------- -------- -------- ................\n" -" 123456a0 -------- -------- -------- -------- ................\n" -" 123456b0 -------- -------- -------- -------- ................\n" -" 123456c0 -------- -------- -------- -------- ................\n" -" 123456d0 -------- -------- -------- -------- ................\n"; +" 12345630 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"; #endif class MemoryMock : public unwindstack::Memory { @@ -513,15 +493,7 @@ TEST_F(DumpMemoryTest, first_read_empty) { const char* expected_dump = \ "\nmemory near r4:\n" #if defined(__LP64__) -R"( 0000000010000f80 ---------------- ---------------- ................ - 0000000010000f90 ---------------- ---------------- ................ - 0000000010000fa0 ---------------- ---------------- ................ - 0000000010000fb0 ---------------- ---------------- ................ - 0000000010000fc0 ---------------- ---------------- ................ - 0000000010000fd0 ---------------- ---------------- ................ - 0000000010000fe0 ---------------- ---------------- ................ - 0000000010000ff0 ---------------- ---------------- ................ - 0000000010001000 8786858483828180 8f8e8d8c8b8a8988 ................ +R"( 0000000010001000 8786858483828180 8f8e8d8c8b8a8988 ................ 0000000010001010 9796959493929190 9f9e9d9c9b9a9998 ................ 0000000010001020 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................ 0000000010001030 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................ @@ -531,15 +503,7 @@ R"( 0000000010000f80 ---------------- ---------------- ................ 0000000010001070 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................ )"; #else -R"( 10000f80 -------- -------- -------- -------- ................ - 10000f90 -------- -------- -------- -------- ................ - 10000fa0 -------- -------- -------- -------- ................ - 10000fb0 -------- -------- -------- -------- ................ - 10000fc0 -------- -------- -------- -------- ................ - 10000fd0 -------- -------- -------- -------- ................ - 10000fe0 -------- -------- -------- -------- ................ - 10000ff0 -------- -------- -------- -------- ................ - 10001000 83828180 87868584 8b8a8988 8f8e8d8c ................ +R"( 10001000 83828180 87868584 8b8a8988 8f8e8d8c ................ 10001010 93929190 97969594 9b9a9998 9f9e9d9c ................ 10001020 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................ 10001030 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................ @@ -574,39 +538,11 @@ TEST_F(DumpMemoryTest, first_read_empty_second_read_stops) { const char* expected_dump = \ "\nmemory near r4:\n" #if defined(__LP64__) -" 0000000010000f40 ---------------- ---------------- ................\n" -" 0000000010000f50 ---------------- ---------------- ................\n" -" 0000000010000f60 ---------------- ---------------- ................\n" -" 0000000010000f70 ---------------- ---------------- ................\n" -" 0000000010000f80 ---------------- ---------------- ................\n" -" 0000000010000f90 ---------------- ---------------- ................\n" -" 0000000010000fa0 ---------------- ---------------- ................\n" -" 0000000010000fb0 ---------------- ---------------- ................\n" -" 0000000010000fc0 ---------------- ---------------- ................\n" -" 0000000010000fd0 ---------------- ---------------- ................\n" -" 0000000010000fe0 ---------------- ---------------- ................\n" -" 0000000010000ff0 ---------------- ---------------- ................\n" " 0000000010001000 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n" -" 0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n" -" 0000000010001020 ---------------- ---------------- ................\n" -" 0000000010001030 ---------------- ---------------- ................\n"; +" 0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"; #else -" 10000f40 -------- -------- -------- -------- ................\n" -" 10000f50 -------- -------- -------- -------- ................\n" -" 10000f60 -------- -------- -------- -------- ................\n" -" 10000f70 -------- -------- -------- -------- ................\n" -" 10000f80 -------- -------- -------- -------- ................\n" -" 10000f90 -------- -------- -------- -------- ................\n" -" 10000fa0 -------- -------- -------- -------- ................\n" -" 10000fb0 -------- -------- -------- -------- ................\n" -" 10000fc0 -------- -------- -------- -------- ................\n" -" 10000fd0 -------- -------- -------- -------- ................\n" -" 10000fe0 -------- -------- -------- -------- ................\n" -" 10000ff0 -------- -------- -------- -------- ................\n" " 10001000 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n" -" 10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n" -" 10001020 -------- -------- -------- -------- ................\n" -" 10001030 -------- -------- -------- -------- ................\n"; +" 10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"; #endif ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp index 23ca070e5..1ab93fe1d 100644 --- a/debuggerd/libdebuggerd/tombstone_proto.cpp +++ b/debuggerd/libdebuggerd/tombstone_proto.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "DEBUG" #include "libdebuggerd/tombstone.h" +#include "libdebuggerd/gwp_asan.h" +#include "libdebuggerd/scudo.h" #include <errno.h> #include <fcntl.h> @@ -106,32 +108,120 @@ static std::optional<std::string> get_stack_overflow_cause(uint64_t fault_addr, return {}; } -static void dump_probable_cause(Tombstone* tombstone, const siginfo_t* si, unwindstack::Maps* maps, - unwindstack::Regs* regs) { +void set_human_readable_cause(Cause* cause, uint64_t fault_addr) { + if (!cause->has_memory_error() || !cause->memory_error().has_heap()) { + return; + } + + const MemoryError& memory_error = cause->memory_error(); + const HeapObject& heap_object = memory_error.heap(); + + const char *tool_str; + switch (memory_error.tool()) { + case MemoryError_Tool_GWP_ASAN: + tool_str = "GWP-ASan"; + break; + case MemoryError_Tool_SCUDO: + tool_str = "MTE"; + break; + default: + tool_str = "Unknown"; + break; + } + + const char *error_type_str; + switch (memory_error.type()) { + case MemoryError_Type_USE_AFTER_FREE: + error_type_str = "Use After Free"; + break; + case MemoryError_Type_DOUBLE_FREE: + error_type_str = "Double Free"; + break; + case MemoryError_Type_INVALID_FREE: + error_type_str = "Invalid (Wild) Free"; + break; + case MemoryError_Type_BUFFER_OVERFLOW: + error_type_str = "Buffer Overflow"; + break; + case MemoryError_Type_BUFFER_UNDERFLOW: + error_type_str = "Buffer Underflow"; + break; + default: + cause->set_human_readable( + StringPrintf("[%s]: Unknown error occurred at 0x%" PRIx64 ".", tool_str, fault_addr)); + return; + } + + uint64_t diff; + const char* location_str; + + if (fault_addr < heap_object.address()) { + // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef. + location_str = "left of"; + diff = heap_object.address() - fault_addr; + } else if (fault_addr - heap_object.address() < heap_object.size()) { + // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef. + location_str = "into"; + diff = fault_addr - heap_object.address(); + } else { + // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef. + location_str = "right of"; + diff = fault_addr - heap_object.address() - heap_object.size(); + } + + // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'. + const char* byte_suffix = "s"; + if (diff == 1) { + byte_suffix = ""; + } + + cause->set_human_readable(StringPrintf( + "[%s]: %s, %" PRIu64 " byte%s %s a %" PRIu64 "-byte allocation at 0x%" PRIx64, tool_str, + error_type_str, diff, byte_suffix, location_str, heap_object.size(), heap_object.address())); +} + +static void dump_probable_cause(Tombstone* tombstone, unwindstack::Unwinder* unwinder, + const ProcessInfo& process_info, const ThreadInfo& main_thread) { + ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info); + if (scudo_crash_data.CrashIsMine()) { + scudo_crash_data.AddCauseProtos(tombstone, unwinder); + return; + } + + GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(), process_info, + main_thread); + if (gwp_asan_crash_data.CrashIsMine()) { + gwp_asan_crash_data.AddCauseProtos(tombstone, unwinder); + return; + } + + const siginfo *si = main_thread.siginfo; + auto fault_addr = reinterpret_cast<uint64_t>(si->si_addr); + unwindstack::Maps* maps = unwinder->GetMaps(); + std::optional<std::string> cause; if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) { - if (si->si_addr < reinterpret_cast<void*>(4096)) { + if (fault_addr < 4096) { cause = "null pointer dereference"; - } else if (si->si_addr == reinterpret_cast<void*>(0xffff0ffc)) { + } else if (fault_addr == 0xffff0ffc) { cause = "call to kuser_helper_version"; - } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fe0)) { + } else if (fault_addr == 0xffff0fe0) { cause = "call to kuser_get_tls"; - } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fc0)) { + } else if (fault_addr == 0xffff0fc0) { cause = "call to kuser_cmpxchg"; - } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fa0)) { + } else if (fault_addr == 0xffff0fa0) { cause = "call to kuser_memory_barrier"; - } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) { + } else if (fault_addr == 0xffff0f60) { cause = "call to kuser_cmpxchg64"; } else { - cause = get_stack_overflow_cause(reinterpret_cast<uint64_t>(si->si_addr), regs->sp(), maps); + cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps); } } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) { - uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr); unwindstack::MapInfo* map_info = maps->Find(fault_addr); if (map_info != nullptr && map_info->flags == PROT_EXEC) { cause = "execute-only (no-read) memory access error; likely due to data in .text."; } else { - cause = get_stack_overflow_cause(fault_addr, regs->sp(), maps); + cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps); } } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) { cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING, @@ -139,7 +229,8 @@ static void dump_probable_cause(Tombstone* tombstone, const siginfo_t* si, unwin } if (cause) { - tombstone->mutable_cause()->set_human_readable(*cause); + Cause *cause_proto = tombstone->add_causes(); + cause_proto->set_human_readable(*cause); } } @@ -205,12 +296,49 @@ static void dump_open_fds(Tombstone* tombstone, const OpenFilesList* open_files) } } +void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame, + unwindstack::Maps* maps) { + f->set_rel_pc(frame.rel_pc); + f->set_pc(frame.pc); + f->set_sp(frame.sp); + + if (!frame.function_name.empty()) { + // TODO: Should this happen here, or on the display side? + char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr); + if (demangled_name) { + f->set_function_name(demangled_name); + free(demangled_name); + } else { + f->set_function_name(frame.function_name); + } + } + + f->set_function_offset(frame.function_offset); + + if (frame.map_start == frame.map_end) { + // No valid map associated with this frame. + f->set_file_name("<unknown>"); + } else if (!frame.map_name.empty()) { + f->set_file_name(frame.map_name); + } else { + f->set_file_name(StringPrintf("<anonymous:%" PRIx64 ">", frame.map_start)); + } + + f->set_file_map_offset(frame.map_elf_start_offset); + + unwindstack::MapInfo* map_info = maps->Find(frame.map_start); + if (map_info) { + f->set_build_id(map_info->GetPrintableBuildID()); + } +} + static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder, const ThreadInfo& thread_info, bool memory_dump = false) { Thread thread; thread.set_id(thread_info.tid); thread.set_name(thread_info.thread_name); + thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl); unwindstack::Maps* maps = unwinder->GetMaps(); unwindstack::Memory* memory = unwinder->GetProcessMemory().get(); @@ -225,20 +353,19 @@ static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder, if (memory_dump) { MemoryDump dump; - char buf[256]; - size_t start_offset = 0; - ssize_t bytes = dump_memory(buf, sizeof(buf), &start_offset, &value, memory); - if (bytes == -1) { - return; - } - dump.set_register_name(name); - unwindstack::MapInfo* map_info = maps->Find(untag_address(value)); if (map_info) { dump.set_mapping_name(map_info->name); } + char buf[256]; + uint8_t tags[256 / kTagGranuleSize]; + size_t start_offset = 0; + ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory); + if (bytes == -1) { + return; + } dump.set_begin_address(value); if (start_offset + bytes > sizeof(buf)) { @@ -246,7 +373,8 @@ static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder, start_offset, bytes); } - dump.set_memory(buf, start_offset + bytes); + dump.set_memory(buf, bytes); + dump.set_tags(tags, bytes / kTagGranuleSize); *thread.add_memory_dump() = std::move(dump); } @@ -267,39 +395,7 @@ static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder, unwinder->SetDisplayBuildID(true); for (const auto& frame : unwinder->frames()) { BacktraceFrame* f = thread.add_current_backtrace(); - f->set_rel_pc(frame.rel_pc); - f->set_pc(frame.pc); - f->set_sp(frame.sp); - - if (!frame.function_name.empty()) { - // TODO: Should this happen here, or on the display side? - char* demangled_name = - __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr); - if (demangled_name) { - f->set_function_name(demangled_name); - free(demangled_name); - } else { - f->set_function_name(frame.function_name); - } - } - - f->set_function_offset(frame.function_offset); - - if (frame.map_start == frame.map_end) { - // No valid map associated with this frame. - f->set_file_name("<unknown>"); - } else if (!frame.map_name.empty()) { - f->set_file_name(frame.map_name); - } else { - f->set_file_name(StringPrintf("<anonymous:%" PRIx64 ">", frame.map_start)); - } - - f->set_file_map_offset(frame.map_elf_start_offset); - - unwindstack::MapInfo* map_info = maps->Find(frame.map_start); - if (map_info) { - f->set_build_id(map_info->GetPrintableBuildID()); - } + fill_in_backtrace_frame(f, frame, maps); } } @@ -458,7 +554,7 @@ void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwind if (process_info.has_fault_address) { sig.set_has_fault_address(true); - sig.set_fault_address(process_info.untagged_fault_address); + sig.set_fault_address(process_info.maybe_tagged_fault_address); } *result.mutable_signal_info() = sig; @@ -473,8 +569,7 @@ void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwind } } - dump_probable_cause(&result, main_thread.siginfo, unwinder->GetMaps(), - main_thread.registers.get()); + dump_probable_cause(&result, unwinder, process_info, main_thread); dump_mappings(&result, unwinder); diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp index 187379daf..e4dd6df1b 100644 --- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp +++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp @@ -74,6 +74,9 @@ static void print_thread_header(CallbackType callback, const Tombstone& tombston CB(should_log, "pid: %d, tid: %d, name: %s >>> %s <<<", tombstone.pid(), thread.id(), thread.name().c_str(), tombstone.process_name().c_str()); CB(should_log, "uid: %d", tombstone.uid()); + if (thread.tagged_addr_ctrl() != -1) { + CB(should_log, "tagged_addr_ctrl: %016" PRIx64, thread.tagged_addr_ctrl()); + } } static void print_register_row(CallbackType callback, int word_size, @@ -136,12 +139,11 @@ static void print_thread_registers(CallbackType callback, const Tombstone& tombs print_register_row(callback, word_size, special_row, should_log); } -static void print_thread_backtrace(CallbackType callback, const Tombstone& tombstone, - const Thread& thread, bool should_log) { - CBS(""); - CB(should_log, "backtrace:"); +static void print_backtrace(CallbackType callback, const Tombstone& tombstone, + const google::protobuf::RepeatedPtrField<BacktraceFrame>& backtrace, + bool should_log) { int index = 0; - for (const auto& frame : thread.current_backtrace()) { + for (const auto& frame : backtrace) { std::string function; if (!frame.function_name().empty()) { @@ -159,16 +161,32 @@ static void print_thread_backtrace(CallbackType callback, const Tombstone& tombs } } +static void print_thread_backtrace(CallbackType callback, const Tombstone& tombstone, + const Thread& thread, bool should_log) { + CBS(""); + CB(should_log, "backtrace:"); + print_backtrace(callback, tombstone, thread.current_backtrace(), should_log); +} + static void print_thread_memory_dump(CallbackType callback, const Tombstone& tombstone, const Thread& thread) { static constexpr size_t bytes_per_line = 16; + static_assert(bytes_per_line == kTagGranuleSize); int word_size = pointer_width(tombstone); for (const auto& mem : thread.memory_dump()) { CBS(""); - CBS("memory near %s (%s):", mem.register_name().c_str(), mem.mapping_name().c_str()); + if (mem.mapping_name().empty()) { + CBS("memory near %s:", mem.register_name().c_str()); + } else { + CBS("memory near %s (%s):", mem.register_name().c_str(), mem.mapping_name().c_str()); + } uint64_t addr = mem.begin_address(); for (size_t offset = 0; offset < mem.memory().size(); offset += bytes_per_line) { - std::string line = StringPrintf(" %0*" PRIx64, word_size * 2, addr + offset); + uint64_t tagged_addr = addr; + if (mem.tags().size() > offset / kTagGranuleSize) { + tagged_addr |= static_cast<uint64_t>(mem.tags()[offset / kTagGranuleSize]) << 56; + } + std::string line = StringPrintf(" %0*" PRIx64, word_size * 2, tagged_addr + offset); size_t bytes = std::min(bytes_per_line, mem.memory().size() - offset); for (size_t i = 0; i < bytes; i += word_size) { @@ -231,9 +249,8 @@ static void print_main_thread(CallbackType callback, const Tombstone& tombstone, sender_desc.c_str(), fault_addr_desc.c_str()); } - if (tombstone.has_cause()) { - const Cause& cause = tombstone.cause(); - CBL("Cause: %s", cause.human_readable().c_str()); + if (tombstone.causes_size() == 1) { + CBL("Cause: %s", tombstone.causes(0).human_readable().c_str()); } if (!tombstone.abort_message().empty()) { @@ -242,6 +259,36 @@ static void print_main_thread(CallbackType callback, const Tombstone& tombstone, print_thread_registers(callback, tombstone, thread, true); print_thread_backtrace(callback, tombstone, thread, true); + + if (tombstone.causes_size() > 1) { + CBS(""); + CBS("Note: multiple potential causes for this crash were detected, listing them in decreasing " + "order of probability."); + } + + for (const Cause& cause : tombstone.causes()) { + if (tombstone.causes_size() > 1) { + CBS(""); + CBS("Cause: %s", cause.human_readable().c_str()); + } + + if (cause.has_memory_error() && cause.memory_error().has_heap()) { + const HeapObject& heap_object = cause.memory_error().heap(); + + if (heap_object.deallocation_backtrace_size() != 0) { + CBS(""); + CBS("deallocated by thread %" PRIu64 ":", heap_object.deallocation_tid()); + print_backtrace(callback, tombstone, heap_object.deallocation_backtrace(), false); + } + + if (heap_object.allocation_backtrace_size() != 0) { + CBS(""); + CBS("allocated by thread %" PRIu64 ":", heap_object.allocation_tid()); + print_backtrace(callback, tombstone, heap_object.allocation_backtrace(), false); + } + } + } + print_thread_memory_dump(callback, tombstone, thread); CBS(""); diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp index 6f13ed4c2..2c645b542 100644 --- a/debuggerd/libdebuggerd/utility.cpp +++ b/debuggerd/libdebuggerd/utility.cpp @@ -125,8 +125,9 @@ void _VLOG(log_t* log, enum logtype ltype, const char* fmt, va_list ap) { #define MEMORY_BYTES_TO_DUMP 256 #define MEMORY_BYTES_PER_LINE 16 +static_assert(MEMORY_BYTES_PER_LINE == kTagGranuleSize); -ssize_t dump_memory(void* out, size_t len, size_t* start_offset, uint64_t* addr, +ssize_t dump_memory(void* out, size_t len, uint8_t* tags, size_t tags_len, uint64_t* addr, unwindstack::Memory* memory) { // Align the address to the number of bytes per line to avoid confusing memory tag output if // memory is tagged and we start from a misaligned address. Start 32 bytes before the address. @@ -154,17 +155,17 @@ ssize_t dump_memory(void* out, size_t len, size_t* start_offset, uint64_t* addr, bytes &= ~(sizeof(uintptr_t) - 1); } - *start_offset = 0; bool skip_2nd_read = false; if (bytes == 0) { // In this case, we might want to try another read at the beginning of // the next page only if it's within the amount of memory we would have // read. size_t page_size = sysconf(_SC_PAGE_SIZE); - *start_offset = ((*addr + (page_size - 1)) & ~(page_size - 1)) - *addr; - if (*start_offset == 0 || *start_offset >= len) { + uint64_t next_page = (*addr + (page_size - 1)) & ~(page_size - 1); + if (next_page == *addr || next_page >= *addr + len) { skip_2nd_read = true; } + *addr = next_page; } if (bytes < len && !skip_2nd_read) { @@ -174,8 +175,7 @@ ssize_t dump_memory(void* out, size_t len, size_t* start_offset, uint64_t* addr, // into a readable map. Only requires one extra read because a map has // to contain at least one page, and the total number of bytes to dump // is smaller than a page. - size_t bytes2 = memory->Read(*addr + *start_offset + bytes, static_cast<uint8_t*>(out) + bytes, - len - bytes - *start_offset); + size_t bytes2 = memory->Read(*addr + bytes, static_cast<uint8_t*>(out) + bytes, len - bytes); bytes += bytes2; if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) { // This should never happen, but we'll try and continue any way. @@ -190,15 +190,24 @@ ssize_t dump_memory(void* out, size_t len, size_t* start_offset, uint64_t* addr, return -1; } + for (uint64_t tag_granule = 0; tag_granule < bytes / kTagGranuleSize; ++tag_granule) { + long tag = memory->ReadTag(*addr + kTagGranuleSize * tag_granule); + if (tag_granule < tags_len) { + tags[tag_granule] = tag >= 0 ? tag : 0; + } else { + ALOGE("Insufficient space for tags"); + } + } + return bytes; } void dump_memory(log_t* log, unwindstack::Memory* memory, uint64_t addr, const std::string& label) { // Dump 256 bytes uintptr_t data[MEMORY_BYTES_TO_DUMP / sizeof(uintptr_t)]; - size_t start_offset = 0; + uint8_t tags[MEMORY_BYTES_TO_DUMP / kTagGranuleSize]; - ssize_t bytes = dump_memory(data, sizeof(data), &start_offset, &addr, memory); + ssize_t bytes = dump_memory(data, sizeof(data), tags, sizeof(tags), &addr, memory); if (bytes == -1) { return; } @@ -212,38 +221,27 @@ void dump_memory(log_t* log, unwindstack::Memory* memory, uint64_t addr, const s // On 32-bit machines, there are still 16 bytes per line but addresses and // words are of course presented differently. uintptr_t* data_ptr = data; - size_t current = 0; - size_t total_bytes = start_offset + bytes; - for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) { - uint64_t tagged_addr = addr; - long tag = memory->ReadTag(addr); - if (tag >= 0) { - tagged_addr |= static_cast<uint64_t>(tag) << 56; - } + uint8_t* tags_ptr = tags; + for (size_t line = 0; line < static_cast<size_t>(bytes) / MEMORY_BYTES_PER_LINE; line++) { + uint64_t tagged_addr = addr | static_cast<uint64_t>(*tags_ptr++) << 56; std::string logline; android::base::StringAppendF(&logline, " %" PRIPTR, tagged_addr); addr += MEMORY_BYTES_PER_LINE; std::string ascii; for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++) { - if (current >= start_offset && current + sizeof(uintptr_t) <= total_bytes) { - android::base::StringAppendF(&logline, " %" PRIPTR, static_cast<uint64_t>(*data_ptr)); - - // Fill out the ascii string from the data. - uint8_t* ptr = reinterpret_cast<uint8_t*>(data_ptr); - for (size_t val = 0; val < sizeof(uintptr_t); val++, ptr++) { - if (*ptr >= 0x20 && *ptr < 0x7f) { - ascii += *ptr; - } else { - ascii += '.'; - } + android::base::StringAppendF(&logline, " %" PRIPTR, static_cast<uint64_t>(*data_ptr)); + + // Fill out the ascii string from the data. + uint8_t* ptr = reinterpret_cast<uint8_t*>(data_ptr); + for (size_t val = 0; val < sizeof(uintptr_t); val++, ptr++) { + if (*ptr >= 0x20 && *ptr < 0x7f) { + ascii += *ptr; + } else { + ascii += '.'; } - data_ptr++; - } else { - logline += ' ' + std::string(sizeof(uintptr_t) * 2, '-'); - ascii += std::string(sizeof(uintptr_t), '.'); } - current += sizeof(uintptr_t); + data_ptr++; } _LOG(log, logtype::MEMORY, "%s %s\n", logline.c_str(), ascii.c_str()); } diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto index 2c7156bcb..433c406ac 100644 --- a/debuggerd/proto/tombstone.proto +++ b/debuggerd/proto/tombstone.proto @@ -21,7 +21,7 @@ message Tombstone { Signal signal_info = 10; string abort_message = 14; - Cause cause = 15; + repeated Cause causes = 15; map<uint32, Thread> threads = 16; repeated MemoryMapping memory_mappings = 17; @@ -57,10 +57,52 @@ message Signal { reserved 10 to 999; } +message HeapObject { + uint64 address = 1; + uint64 size = 2; + + uint64 allocation_tid = 3; + repeated BacktraceFrame allocation_backtrace = 4; + + uint64 deallocation_tid = 5; + repeated BacktraceFrame deallocation_backtrace = 6; +} + +message MemoryError { + enum Tool { + GWP_ASAN = 0; + SCUDO = 1; + + reserved 2 to 999; + } + Tool tool = 1; + + enum Type { + UNKNOWN = 0; + USE_AFTER_FREE = 1; + DOUBLE_FREE = 2; + INVALID_FREE = 3; + BUFFER_OVERFLOW = 4; + BUFFER_UNDERFLOW = 5; + + reserved 6 to 999; + } + Type type = 2; + + oneof location { + HeapObject heap = 3; + } + + reserved 4 to 999; +} + message Cause { string human_readable = 1; + oneof details { + MemoryError memory_error = 2; + } - reserved 2 to 999; + reserved 3 to 999; } message Register { @@ -76,8 +118,9 @@ message Thread { repeated Register registers = 3; repeated BacktraceFrame current_backtrace = 4; repeated MemoryDump memory_dump = 5; + int64 tagged_addr_ctrl = 6; - reserved 6 to 999; + reserved 7 to 999; } message BacktraceFrame { @@ -100,8 +143,9 @@ message MemoryDump { string mapping_name = 2; uint64 begin_address = 3; bytes memory = 4; + bytes tags = 5; - reserved 5 to 999; + reserved 6 to 999; } message MemoryMapping { |