From 94dc7eb437e22775cff2a056ae04ba3b70bfc29f Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Fri, 29 Mar 2019 15:22:11 -0700 Subject: [PATCH] Collect abort messages on Android. As of Android Q, the android_set_abort_message() function copies the abort message into a mapping with a specific name that starts with a magic number. This makes it possible for Crashpad to collect the abort message by looking for the mapping with this name in procmaps and checking for the magic number. The abort message is stored in a process annotation named "abort_message". Test: No regressions in build/run_tests.py on devices running P and Q Test: Patched into Chromium; manually verified that HWASAN crash report appears in minidump Bug: crashpad:287 Change-Id: I23c4d9e11015c84341de2d2e47e38a1eec508a36 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/1544875 Commit-Queue: Peter Collingbourne Reviewed-by: Joshua Peraza --- client/crashpad_client_linux_test.cc | 32 ++++++++ .../linux/crash_report_exception_handler.cc | 4 +- snapshot/linux/process_reader_linux.cc | 75 +++++++++++++++++++ snapshot/linux/process_reader_linux.h | 8 ++ snapshot/linux/process_reader_linux_test.cc | 33 ++++++++ snapshot/linux/process_snapshot_linux.cc | 10 +++ snapshot/linux/process_snapshot_linux.h | 16 ++-- 7 files changed, 170 insertions(+), 8 deletions(-) diff --git a/client/crashpad_client_linux_test.cc b/client/crashpad_client_linux_test.cc index 1f80d25f..f61ea44f 100644 --- a/client/crashpad_client_linux_test.cc +++ b/client/crashpad_client_linux_test.cc @@ -14,6 +14,7 @@ #include "client/crashpad_client.h" +#include #include #include #include @@ -41,6 +42,15 @@ #include "util/misc/from_pointer_cast.h" #include "util/posix/signals.h" +#if defined(OS_ANDROID) +#include +#include "dlfcn_internal.h" + +// Normally this comes from set_abort_message.h, but only at API level 21. +extern "C" void android_set_abort_message(const char* msg) + __attribute__((weak)); +#endif + namespace crashpad { namespace test { namespace { @@ -103,10 +113,26 @@ TEST(CrashpadClient, SimulateCrash) { constexpr char kTestAnnotationName[] = "name_of_annotation"; constexpr char kTestAnnotationValue[] = "value_of_annotation"; +#if defined(OS_ANDROID) +constexpr char kTestAbortMessage[] = "test abort message"; +#endif + void ValidateDump(const CrashReportDatabase::UploadReport* report) { ProcessSnapshotMinidump minidump_snapshot; ASSERT_TRUE(minidump_snapshot.Initialize(report->Reader())); +#if defined(OS_ANDROID) + // This part of the test requires Q. The API level on Q devices will be 28 + // until the API is finalized, so we can't check API level yet. For now, test + // for the presence of a libc symbol which was introduced in Q. + if (crashpad::internal::Dlsym(RTLD_DEFAULT, "android_fdsan_close_with_tag")) { + const auto& annotations = minidump_snapshot.AnnotationsSimpleMap(); + auto abort_message = annotations.find("abort_message"); + ASSERT_NE(annotations.end(), abort_message); + EXPECT_EQ(kTestAbortMessage, abort_message->second); + } +#endif + for (const ModuleSnapshot* module : minidump_snapshot.Modules()) { for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) { if (static_cast(annotation.type) != @@ -153,6 +179,12 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) { return EXIT_FAILURE; } +#if defined(OS_ANDROID) + if (android_set_abort_message) { + android_set_abort_message(kTestAbortMessage); + } +#endif + __builtin_trap(); NOTREACHED(); diff --git a/handler/linux/crash_report_exception_handler.cc b/handler/linux/crash_report_exception_handler.cc index 71a7009b..910b2884 100644 --- a/handler/linux/crash_report_exception_handler.cc +++ b/handler/linux/crash_report_exception_handler.cc @@ -107,7 +107,9 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection( } process_snapshot.SetClientID(client_id); - process_snapshot.SetAnnotationsSimpleMap(*process_annotations_); + for (auto& p : *process_annotations_) { + process_snapshot.AddAnnotation(p.first, p.second); + } std::unique_ptr new_report; CrashReportDatabase::OperationStatus database_status = diff --git a/snapshot/linux/process_reader_linux.cc b/snapshot/linux/process_reader_linux.cc index c1d6c40f..b96abfe7 100644 --- a/snapshot/linux/process_reader_linux.cc +++ b/snapshot/linux/process_reader_linux.cc @@ -280,6 +280,81 @@ const std::vector& ProcessReaderLinux::Modules() { return modules_; } +void ProcessReaderLinux::InitializeAbortMessage() { +#if defined(OS_ANDROID) + const MemoryMap::Mapping* mapping = + memory_map_.FindMappingWithName("[anon:abort message]"); + if (!mapping) { + return; + } + + if (is_64_bit_) { + ReadAbortMessage(mapping); + } else { + ReadAbortMessage(mapping); + } +#endif +} + +#if defined(OS_ANDROID) + +// These structure definitions and the magic numbers below were copied from +// bionic/libc/bionic/android_set_abort_message.cpp + +template +struct abort_msg_t { + uint32_t size; + char msg[0]; +}; + +template <> +struct abort_msg_t { + uint64_t size; + char msg[0]; +}; + +template +struct magic_abort_msg_t { + uint64_t magic1; + uint64_t magic2; + abort_msg_t msg; +}; + +template +void ProcessReaderLinux::ReadAbortMessage(const MemoryMap::Mapping* mapping) { + magic_abort_msg_t header; + if (!Memory()->Read( + mapping->range.Base(), sizeof(magic_abort_msg_t), &header)) { + return; + } + + size_t size = header.msg.size - sizeof(magic_abort_msg_t) - 1; + if (header.magic1 != 0xb18e40886ac388f0ULL || + header.magic2 != 0xc6dfba755a1de0b5ULL || + mapping->range.Size() < + offsetof(magic_abort_msg_t, msg.msg) + size) { + return; + } + + abort_message_.resize(size); + if (!Memory()->Read( + mapping->range.Base() + offsetof(magic_abort_msg_t, msg.msg), + size, + &abort_message_[0])) { + abort_message_.clear(); + } +} + +#endif // OS_ANDROID + +const std::string& ProcessReaderLinux::AbortMessage() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (abort_message_.empty()) { + InitializeAbortMessage(); + } + return abort_message_; +} + void ProcessReaderLinux::InitializeThreads() { DCHECK(threads_.empty()); initialized_threads_ = true; diff --git a/snapshot/linux/process_reader_linux.h b/snapshot/linux/process_reader_linux.h index 1b30f53e..258e102a 100644 --- a/snapshot/linux/process_reader_linux.h +++ b/snapshot/linux/process_reader_linux.h @@ -153,15 +153,23 @@ class ProcessReaderLinux { //! `0`) corresponds to the main executable. const std::vector& Modules(); + //! \return On Android, the abort message that was passed to + //! android_set_abort_message(). This is only available on Q or later. + const std::string& AbortMessage(); + private: void InitializeThreads(); void InitializeModules(); + void InitializeAbortMessage(); + template + void ReadAbortMessage(const MemoryMap::Mapping* mapping); PtraceConnection* connection_; // weak ProcessInfo process_info_; MemoryMap memory_map_; std::vector threads_; std::vector modules_; + std::string abort_message_; std::vector> elf_readers_; bool is_64_bit_; bool initialized_threads_; diff --git a/snapshot/linux/process_reader_linux_test.cc b/snapshot/linux/process_reader_linux_test.cc index e2e5566c..e25b8023 100644 --- a/snapshot/linux/process_reader_linux_test.cc +++ b/snapshot/linux/process_reader_linux_test.cc @@ -38,6 +38,7 @@ #include "build/build_config.h" #include "gtest/gtest.h" #include "test/errors.h" +#include "test/gtest_disabled.h" #include "test/linux/fake_ptrace_connection.h" #include "test/linux/get_tls.h" #include "test/multiprocess.h" @@ -53,6 +54,12 @@ #if defined(OS_ANDROID) #include +#include +#include "dlfcn_internal.h" + +// Normally this comes from set_abort_message.h, but only at API level 21. +extern "C" void android_set_abort_message(const char* msg) + __attribute__((weak)); #endif namespace crashpad { @@ -86,6 +93,8 @@ TEST(ProcessReaderLinux, SelfBasic) { sizeof(kTestMemory), &buffer)); EXPECT_STREQ(kTestMemory, buffer); + + EXPECT_EQ("", process_reader.AbortMessage()); } constexpr char kTestMemory[] = "Read me from another process"; @@ -778,6 +787,30 @@ TEST(ProcessReaderLinux, ChildModules) { test.Run(); } +#if defined(OS_ANDROID) +const char kTestAbortMessage[] = "test abort message"; + +TEST(ProcessReaderLinux, AbortMessage) { + // This test requires Q. The API level on Q devices will be 28 until the API + // is finalized, so we can't check API level yet. For now, test for the + // presence of a libc symbol which was introduced in Q. + if (!crashpad::internal::Dlsym(RTLD_DEFAULT, + "android_fdsan_close_with_tag")) { + DISABLED_TEST(); + } + + android_set_abort_message(kTestAbortMessage); + + FakePtraceConnection connection; + connection.Initialize(getpid()); + + ProcessReaderLinux process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + EXPECT_EQ(kTestAbortMessage, process_reader.AbortMessage()); +} +#endif + } // namespace } // namespace test } // namespace crashpad diff --git a/snapshot/linux/process_snapshot_linux.cc b/snapshot/linux/process_snapshot_linux.cc index 041653a3..4141b2f4 100644 --- a/snapshot/linux/process_snapshot_linux.cc +++ b/snapshot/linux/process_snapshot_linux.cc @@ -43,6 +43,7 @@ bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) { InitializeThreads(); InitializeModules(); + InitializeAnnotations(); INITIALIZATION_STATE_SET_VALID(initialized_); return true; @@ -265,4 +266,13 @@ void ProcessSnapshotLinux::InitializeModules() { } } +void ProcessSnapshotLinux::InitializeAnnotations() { +#if defined(OS_ANDROID) + const std::string& abort_message = process_reader_.AbortMessage(); + if (!abort_message.empty()) { + annotations_simple_map_["abort_message"] = abort_message; + } +#endif +} + } // namespace crashpad diff --git a/snapshot/linux/process_snapshot_linux.h b/snapshot/linux/process_snapshot_linux.h index 236926d6..84bd226a 100644 --- a/snapshot/linux/process_snapshot_linux.h +++ b/snapshot/linux/process_snapshot_linux.h @@ -78,15 +78,16 @@ class ProcessSnapshotLinux final : public ProcessSnapshot { //! ClientID() will return an identifier consisting entirely of zeroes. void SetClientID(const UUID& client_id) { client_id_ = client_id; } - //! \brief Sets the value to be returned by AnnotationsSimpleMap(). + //! \brief Add an annotation to be returned by AnnotationsSimpleMap(). //! - //! All process annotations are under the control of the snapshot + //! Most process annotations are under the control of the snapshot //! producer, which may call this method to establish these annotations. - //! Contrast this with module annotations, which are under the control of the - //! process being snapshotted. - void SetAnnotationsSimpleMap( - const std::map& annotations_simple_map) { - annotations_simple_map_ = annotations_simple_map; + //! On Android Q or later, the process snapshot may add an "abort_message" + //! annotation, which will contain the abort message passed to the + //! android_set_abort_message() function. Contrast this with module + //! annotations, which are under the control of the process being snapshotted. + void AddAnnotation(const std::string& key, const std::string& value) { + annotations_simple_map_[key] = value; } //! \brief Returns options from CrashpadInfo structures found in modules in @@ -120,6 +121,7 @@ class ProcessSnapshotLinux final : public ProcessSnapshot { private: void InitializeThreads(); void InitializeModules(); + void InitializeAnnotations(); std::map annotations_simple_map_; timeval snapshot_time_;