diff options
Diffstat (limited to 'debuggerd/libdebuggerd')
-rw-r--r-- | debuggerd/libdebuggerd/gwp_asan.cpp | 81 | ||||
-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/types.h | 1 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/include/libdebuggerd/utility.h | 5 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/scudo.cpp | 71 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/test/UnwinderMock.h | 3 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/test/dump_memory_test.cpp | 76 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/test/sys/system_properties.h | 37 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/test/tombstone_test.cpp | 10 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/tombstone_proto.cpp | 236 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/tombstone_proto_to_text.cpp | 68 | ||||
-rw-r--r-- | debuggerd/libdebuggerd/utility.cpp | 62 |
14 files changed, 435 insertions, 235 deletions
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp index 9750fc4b0..3ee309fc5 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."); @@ -119,13 +183,6 @@ void GwpAsanCrashData::DumpCause(log_t* log) const { uintptr_t alloc_address = __gwp_asan_get_allocation_address(responsible_allocation_); size_t alloc_size = __gwp_asan_get_allocation_size(responsible_allocation_); - if (crash_address_ == alloc_address) { - // Use After Free on a 41-byte allocation at 0xdeadbeef. - _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s on a %zu-byte allocation at 0x%" PRIxPTR "\n", - error_string_, alloc_size, alloc_address); - return; - } - uintptr_t diff; const char* location_str; @@ -157,8 +214,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 +226,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 +238,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 +253,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 +265,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/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h index d5b07355f..dcb52f92a 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h @@ -46,6 +46,7 @@ struct ProcessInfo { uintptr_t gwp_asan_metadata = 0; uintptr_t scudo_stack_depot = 0; uintptr_t scudo_region_info = 0; + uintptr_t scudo_ring_buffer = 0; bool has_fault_address = false; uintptr_t untagged_fault_address = 0; 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 141c3bd18..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) { @@ -43,6 +44,8 @@ ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory, __scudo_get_stack_depot_size()); auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info, __scudo_get_region_info_size()); + auto ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer, + __scudo_get_ring_buffer_size()); untagged_fault_addr_ = process_info.untagged_fault_address; uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1); @@ -68,14 +71,66 @@ ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory, } __scudo_get_error_info(&error_info_, process_info.maybe_tagged_fault_address, stack_depot.get(), - region_info.get(), memory.get(), memory_tags.get(), memory_begin, - memory_end - memory_begin); + region_info.get(), ring_buffer.get(), memory.get(), memory_tags.get(), + memory_begin, memory_end - memory_begin); } 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, @@ -138,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; @@ -149,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/UnwinderMock.h b/debuggerd/libdebuggerd/test/UnwinderMock.h index 44a9214e5..8f84346af 100644 --- a/debuggerd/libdebuggerd/test/UnwinderMock.h +++ b/debuggerd/libdebuggerd/test/UnwinderMock.h @@ -33,8 +33,7 @@ class UnwinderMock : public unwindstack::Unwinder { void MockSetBuildID(uint64_t offset, const std::string& build_id) { unwindstack::MapInfo* map_info = GetMaps()->Find(offset); if (map_info != nullptr) { - std::string* new_build_id = new std::string(build_id); - map_info->build_id = new_build_id; + map_info->SetBuildID(std::string(build_id)); } } }; 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/test/sys/system_properties.h b/debuggerd/libdebuggerd/test/sys/system_properties.h deleted file mode 100644 index 1f4f58afc..000000000 --- a/debuggerd/libdebuggerd/test/sys/system_properties.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H -#define _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H - -// This is just enough to get the property code to compile on -// the host. - -#define PROP_VALUE_MAX 92 - -#endif // _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp index 7fe8f8297..79ac122a7 100644 --- a/debuggerd/libdebuggerd/test/tombstone_test.cpp +++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp @@ -388,9 +388,8 @@ TEST_F(TombstoneTest, gwp_asan_cause_uaf_exact) { ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); std::string tombstone_contents; ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - ASSERT_THAT(tombstone_contents, - MatchesRegex("Cause: \\[GWP-ASan\\]: Use After Free on a 32-byte " - "allocation at 0x[a-fA-F0-9]+\n")); + ASSERT_THAT(tombstone_contents, MatchesRegex("Cause: \\[GWP-ASan\\]: Use After Free, 0 bytes " + "into a 32-byte allocation at 0x[a-fA-F0-9]+\n")); } TEST_F(TombstoneTest, gwp_asan_cause_double_free) { @@ -405,9 +404,8 @@ TEST_F(TombstoneTest, gwp_asan_cause_double_free) { ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); std::string tombstone_contents; ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - ASSERT_THAT(tombstone_contents, - MatchesRegex("Cause: \\[GWP-ASan\\]: Double Free on a 32-byte " - "allocation at 0x[a-fA-F0-9]+\n")); + ASSERT_THAT(tombstone_contents, MatchesRegex("Cause: \\[GWP-ASan\\]: Double Free, 0 bytes into a " + "32-byte allocation at 0x[a-fA-F0-9]+\n")); } TEST_F(TombstoneTest, gwp_asan_cause_overflow) { diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp index 23ca070e5..3444e29e1 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> @@ -29,10 +31,12 @@ #include <time.h> #include <memory> +#include <optional> #include <string> #include <async_safe/log.h> +#include <android-base/file.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> @@ -45,6 +49,7 @@ #include <log/logprint.h> #include <private/android_filesystem_config.h> +#include <procinfo/process.h> #include <unwindstack/Maps.h> #include <unwindstack/Memory.h> #include <unwindstack/Regs.h> @@ -106,32 +111,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 +232,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 +299,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 +356,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 +376,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 +398,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); } } @@ -423,6 +522,14 @@ static void dump_logcat(Tombstone* tombstone, pid_t pid) { dump_log_file(tombstone, "main", pid); } +static std::optional<uint64_t> read_uptime_secs() { + std::string uptime; + if (!android::base::ReadFileToString("/proc/uptime", &uptime)) { + return {}; + } + return strtoll(uptime.c_str(), nullptr, 10); +} + void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder, const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread, const ProcessInfo& process_info, const OpenFilesList* open_files) { @@ -433,6 +540,22 @@ void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwind result.set_revision(android::base::GetProperty("ro.revision", "unknown")); result.set_timestamp(get_timestamp()); + std::optional<uint64_t> system_uptime = read_uptime_secs(); + if (system_uptime) { + android::procinfo::ProcessInfo proc_info; + std::string error; + if (android::procinfo::GetProcessInfo(target_thread, &proc_info, &error)) { + uint64_t starttime = proc_info.starttime / sysconf(_SC_CLK_TCK); + result.set_process_uptime(*system_uptime - starttime); + } else { + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read process info: %s", + error.c_str()); + } + } else { + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read /proc/uptime: %s", + strerror(errno)); + } + const ThreadInfo& main_thread = threads.at(target_thread); result.set_pid(main_thread.pid); result.set_tid(main_thread.tid); @@ -458,7 +581,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 +596,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..00ca7c18f 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(""); @@ -313,6 +360,7 @@ bool tombstone_proto_to_text(const Tombstone& tombstone, CallbackType callback) CBL("Revision: '%s'", tombstone.revision().c_str()); CBL("ABI: '%s'", abi_string(tombstone)); CBL("Timestamp: %s", tombstone.timestamp().c_str()); + CBL("Process uptime: %ds", tombstone.process_uptime()); // Process header const auto& threads = tombstone.threads(); 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()); } |