Loading debuggerd/debuggerd_test.cpp +81 −10 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ #include <android/fdsan.h> #include <android/set_abort_message.h> #include <bionic/malloc.h> #include <bionic/mte.h> #include <bionic/reserved_signals.h> Loading Loading @@ -98,6 +99,13 @@ constexpr char kWaitForDebuggerKey[] = "debug.debuggerd.wait_for_debugger"; ASSERT_MATCH(result, \ R"(#\d\d pc [0-9a-f]+\s+ \S+ (\(offset 0x[0-9a-f]+\) )?\()" frame_name R"(\+)"); // Enable GWP-ASan at the start of this process. GWP-ASan is enabled using // process sampling, so we need to ensure we force GWP-ASan on. __attribute__((constructor)) static void enable_gwp_asan() { bool force = true; android_mallopt(M_INITIALIZE_GWP_ASAN, &force, sizeof(force)); } static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd, InterceptStatus* status, DebuggerdDumpType intercept_type) { intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName, Loading Loading @@ -397,6 +405,72 @@ static void SetTagCheckingLevelSync() { } #endif // Number of iterations required to reliably guarantee a GWP-ASan crash. // GWP-ASan's sample rate is not truly nondeterministic, it initialises a // thread-local counter at 2*SampleRate, and decrements on each malloc(). Once // the counter reaches zero, we provide a sampled allocation. Then, double that // figure to allow for left/right allocation alignment, as this is done randomly // without bias. #define GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH (0x20000) struct GwpAsanTestParameters { size_t alloc_size; bool free_before_access; int access_offset; std::string cause_needle; // Needle to be found in the "Cause: [GWP-ASan]" line. }; struct GwpAsanCrasherTest : CrasherTest, testing::WithParamInterface<GwpAsanTestParameters> {}; GwpAsanTestParameters gwp_asan_tests[] = { {/* alloc_size */ 7, /* free_before_access */ true, /* access_offset */ 0, "Use After Free, 0 bytes into a 7-byte allocation"}, {/* alloc_size */ 7, /* free_before_access */ true, /* access_offset */ 1, "Use After Free, 1 byte into a 7-byte allocation"}, {/* alloc_size */ 7, /* free_before_access */ false, /* access_offset */ 16, "Buffer Overflow, 9 bytes right of a 7-byte allocation"}, {/* alloc_size */ 16, /* free_before_access */ false, /* access_offset */ -1, "Buffer Underflow, 1 byte left of a 16-byte allocation"}, }; INSTANTIATE_TEST_SUITE_P(GwpAsanTests, GwpAsanCrasherTest, testing::ValuesIn(gwp_asan_tests)); TEST_P(GwpAsanCrasherTest, gwp_asan_uaf) { if (mte_supported()) { // Skip this test on MTE hardware, as MTE will reliably catch these errors // instead of GWP-ASan. GTEST_SKIP() << "Skipped on MTE."; } GwpAsanTestParameters params = GetParam(); int intercept_result; unique_fd output_fd; StartProcess([¶ms]() { for (unsigned i = 0; i < GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH; ++i) { volatile char* p = reinterpret_cast<volatile char*>(malloc(params.alloc_size)); if (params.free_before_access) free(static_cast<void*>(const_cast<char*>(p))); p[params.access_offset] = 42; if (!params.free_before_access) free(static_cast<void*>(const_cast<char*>(p))); } }); StartIntercept(&output_fd); FinishCrasher(); AssertDeath(SIGSEGV); FinishIntercept(&intercept_result); ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; std::string result; ConsumeFd(std::move(output_fd), &result); ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\))"); ASSERT_MATCH(result, R"(Cause: \[GWP-ASan\]: )" + params.cause_needle); if (params.free_before_access) { ASSERT_MATCH(result, R"(deallocated by thread .* #00 pc)"); } ASSERT_MATCH(result, R"(allocated by thread .* #00 pc)"); } struct SizeParamCrasherTest : CrasherTest, testing::WithParamInterface<size_t> {}; INSTANTIATE_TEST_SUITE_P(Sizes, SizeParamCrasherTest, testing::Values(16, 131072)); Loading Loading @@ -428,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 Loading Loading @@ -465,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"; Loading Loading @@ -500,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"; Loading debuggerd/libdebuggerd/gwp_asan.cpp +68 −13 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ #include "libdebuggerd/gwp_asan.h" #include "libdebuggerd/tombstone.h" #include "libdebuggerd/utility.h" #include "gwp_asan/common.h" Loading @@ -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, Loading Loading @@ -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."); Loading @@ -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; Loading Loading @@ -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_)) { Loading @@ -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); Loading @@ -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()); } Loading @@ -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); Loading @@ -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()); } Loading debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h +5 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ #include "types.h" #include "utility.h" class Cause; class Tombstone; class GwpAsanCrashData { public: GwpAsanCrashData() = delete; Loading Loading @@ -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; Loading debuggerd/libdebuggerd/include/libdebuggerd/scudo.h +7 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ #include "scudo/interface.h" class Cause; class Tombstone; class ScudoCrashData { public: ScudoCrashData() = delete; Loading @@ -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_ = {}; Loading @@ -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; }; debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h +8 −0 Original line number Diff line number Diff line Loading @@ -31,9 +31,13 @@ #include "types.h" // Forward declarations class BacktraceFrame; class Cause; class Tombstone; namespace unwindstack { struct FrameData; class Maps; class Unwinder; } Loading Loading @@ -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 Loading
debuggerd/debuggerd_test.cpp +81 −10 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ #include <android/fdsan.h> #include <android/set_abort_message.h> #include <bionic/malloc.h> #include <bionic/mte.h> #include <bionic/reserved_signals.h> Loading Loading @@ -98,6 +99,13 @@ constexpr char kWaitForDebuggerKey[] = "debug.debuggerd.wait_for_debugger"; ASSERT_MATCH(result, \ R"(#\d\d pc [0-9a-f]+\s+ \S+ (\(offset 0x[0-9a-f]+\) )?\()" frame_name R"(\+)"); // Enable GWP-ASan at the start of this process. GWP-ASan is enabled using // process sampling, so we need to ensure we force GWP-ASan on. __attribute__((constructor)) static void enable_gwp_asan() { bool force = true; android_mallopt(M_INITIALIZE_GWP_ASAN, &force, sizeof(force)); } static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd, InterceptStatus* status, DebuggerdDumpType intercept_type) { intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName, Loading Loading @@ -397,6 +405,72 @@ static void SetTagCheckingLevelSync() { } #endif // Number of iterations required to reliably guarantee a GWP-ASan crash. // GWP-ASan's sample rate is not truly nondeterministic, it initialises a // thread-local counter at 2*SampleRate, and decrements on each malloc(). Once // the counter reaches zero, we provide a sampled allocation. Then, double that // figure to allow for left/right allocation alignment, as this is done randomly // without bias. #define GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH (0x20000) struct GwpAsanTestParameters { size_t alloc_size; bool free_before_access; int access_offset; std::string cause_needle; // Needle to be found in the "Cause: [GWP-ASan]" line. }; struct GwpAsanCrasherTest : CrasherTest, testing::WithParamInterface<GwpAsanTestParameters> {}; GwpAsanTestParameters gwp_asan_tests[] = { {/* alloc_size */ 7, /* free_before_access */ true, /* access_offset */ 0, "Use After Free, 0 bytes into a 7-byte allocation"}, {/* alloc_size */ 7, /* free_before_access */ true, /* access_offset */ 1, "Use After Free, 1 byte into a 7-byte allocation"}, {/* alloc_size */ 7, /* free_before_access */ false, /* access_offset */ 16, "Buffer Overflow, 9 bytes right of a 7-byte allocation"}, {/* alloc_size */ 16, /* free_before_access */ false, /* access_offset */ -1, "Buffer Underflow, 1 byte left of a 16-byte allocation"}, }; INSTANTIATE_TEST_SUITE_P(GwpAsanTests, GwpAsanCrasherTest, testing::ValuesIn(gwp_asan_tests)); TEST_P(GwpAsanCrasherTest, gwp_asan_uaf) { if (mte_supported()) { // Skip this test on MTE hardware, as MTE will reliably catch these errors // instead of GWP-ASan. GTEST_SKIP() << "Skipped on MTE."; } GwpAsanTestParameters params = GetParam(); int intercept_result; unique_fd output_fd; StartProcess([¶ms]() { for (unsigned i = 0; i < GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH; ++i) { volatile char* p = reinterpret_cast<volatile char*>(malloc(params.alloc_size)); if (params.free_before_access) free(static_cast<void*>(const_cast<char*>(p))); p[params.access_offset] = 42; if (!params.free_before_access) free(static_cast<void*>(const_cast<char*>(p))); } }); StartIntercept(&output_fd); FinishCrasher(); AssertDeath(SIGSEGV); FinishIntercept(&intercept_result); ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; std::string result; ConsumeFd(std::move(output_fd), &result); ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\))"); ASSERT_MATCH(result, R"(Cause: \[GWP-ASan\]: )" + params.cause_needle); if (params.free_before_access) { ASSERT_MATCH(result, R"(deallocated by thread .* #00 pc)"); } ASSERT_MATCH(result, R"(allocated by thread .* #00 pc)"); } struct SizeParamCrasherTest : CrasherTest, testing::WithParamInterface<size_t> {}; INSTANTIATE_TEST_SUITE_P(Sizes, SizeParamCrasherTest, testing::Values(16, 131072)); Loading Loading @@ -428,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 Loading Loading @@ -465,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"; Loading Loading @@ -500,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"; Loading
debuggerd/libdebuggerd/gwp_asan.cpp +68 −13 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ #include "libdebuggerd/gwp_asan.h" #include "libdebuggerd/tombstone.h" #include "libdebuggerd/utility.h" #include "gwp_asan/common.h" Loading @@ -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, Loading Loading @@ -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."); Loading @@ -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; Loading Loading @@ -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_)) { Loading @@ -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); Loading @@ -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()); } Loading @@ -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); Loading @@ -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()); } Loading
debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h +5 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ #include "types.h" #include "utility.h" class Cause; class Tombstone; class GwpAsanCrashData { public: GwpAsanCrashData() = delete; Loading Loading @@ -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; Loading
debuggerd/libdebuggerd/include/libdebuggerd/scudo.h +7 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ #include "scudo/interface.h" class Cause; class Tombstone; class ScudoCrashData { public: ScudoCrashData() = delete; Loading @@ -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_ = {}; Loading @@ -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; };
debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h +8 −0 Original line number Diff line number Diff line Loading @@ -31,9 +31,13 @@ #include "types.h" // Forward declarations class BacktraceFrame; class Cause; class Tombstone; namespace unwindstack { struct FrameData; class Maps; class Unwinder; } Loading Loading @@ -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