Merge "Support MTE and GWP-ASan features in proto tombstones."
This commit is contained in:
commit
f4a40c0edd
12 changed files with 451 additions and 194 deletions
|
@ -502,12 +502,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
|
||||
|
@ -539,9 +538,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";
|
||||
|
@ -574,9 +572,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";
|
||||
|
|
|
@ -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.");
|
||||
|
@ -150,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_)) {
|
||||
|
@ -164,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);
|
||||
|
||||
|
@ -176,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());
|
||||
}
|
||||
|
@ -191,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);
|
||||
|
||||
|
@ -203,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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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("");
|
||||
|
|
|
@ -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));
|
||||
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 += '.';
|
||||
}
|
||||
// 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());
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue