[GWP-ASan] Add GWP-ASan information to tombstones.

GWP-ASan can provide information about a crash that it caused. Grab the
GWP-ASan regions from the globals shared by the linker for crash-handler
purpopses, pull the information from GWP-ASan, and display it.

This adds two regions:
 1. Causality tracking by GWP-ASan. We now print a cause header about
 the crash, like `Cause: [GWP-ASan]: Use After Free on a 1-byte
 allocation at 0x7365bb3ff8`
 2. Allocation and deallocation stack traces.

Bug: 135634846
Test: atest debuggerd_test

Change-Id: Id28d5400c9a9a053fcde83a4788f971e677d4643
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
new file mode 100644
index 0000000..53df783
--- /dev/null
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libdebuggerd/gwp_asan.h"
+#include "libdebuggerd/utility.h"
+
+#include "gwp_asan/common.h"
+#include "gwp_asan/crash_handler.h"
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.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,
+                                    gwp_asan::AllocatorState* state) {
+  return process_memory->ReadFully(state_addr, state, sizeof(*state));
+}
+
+// Retrieve the GWP-ASan metadata pool from `metadata_addr` inside the process
+// at `process_memory`. The number of metadata slots is retrieved from the
+// allocator state provided. This function returns a heap-allocated copy of the
+// metadata pool whose ownership should be managed by the caller. Returns
+// nullptr on failure.
+static const gwp_asan::AllocationMetadata* retrieve_gwp_asan_metadata(
+    unwindstack::Memory* process_memory, const gwp_asan::AllocatorState& state,
+    uintptr_t metadata_addr) {
+  if (state.MaxSimultaneousAllocations > 1024) {
+    ALOGE(
+        "Error when retrieving GWP-ASan metadata, MSA from state (%zu) "
+        "exceeds maximum allowed (1024).",
+        state.MaxSimultaneousAllocations);
+    return nullptr;
+  }
+
+  gwp_asan::AllocationMetadata* meta =
+      new gwp_asan::AllocationMetadata[state.MaxSimultaneousAllocations];
+  if (!process_memory->ReadFully(metadata_addr, meta,
+                                 sizeof(*meta) * state.MaxSimultaneousAllocations)) {
+    ALOGE(
+        "Error when retrieving GWP-ASan metadata, could not retrieve %zu "
+        "pieces of metadata.",
+        state.MaxSimultaneousAllocations);
+    delete[] meta;
+    meta = nullptr;
+  }
+  return meta;
+}
+
+GwpAsanCrashData::GwpAsanCrashData(unwindstack::Memory* process_memory,
+                                   uintptr_t gwp_asan_state_ptr, uintptr_t gwp_asan_metadata_ptr,
+                                   const ThreadInfo& thread_info) {
+  if (!process_memory || !gwp_asan_metadata_ptr || !gwp_asan_state_ptr) return;
+  // Extract the GWP-ASan regions from the dead process.
+  if (!retrieve_gwp_asan_state(process_memory, gwp_asan_state_ptr, &state_)) return;
+  metadata_.reset(retrieve_gwp_asan_metadata(process_memory, state_, gwp_asan_metadata_ptr));
+  if (!metadata_.get()) return;
+
+  // Get the external crash address from the thread info.
+  crash_address_ = 0u;
+  if (signal_has_si_addr(thread_info.siginfo)) {
+    crash_address_ = reinterpret_cast<uintptr_t>(thread_info.siginfo->si_addr);
+  }
+
+  // Ensure the error belongs to GWP-ASan.
+  if (!__gwp_asan_error_is_mine(&state_, crash_address_)) return;
+
+  is_gwp_asan_responsible_ = true;
+  thread_id_ = thread_info.tid;
+
+  // Grab the internal error address, if it exists.
+  uintptr_t internal_crash_address = __gwp_asan_get_internal_crash_address(&state_);
+  if (internal_crash_address) {
+    crash_address_ = internal_crash_address;
+  }
+
+  // Get other information from the internal state.
+  error_ = __gwp_asan_diagnose_error(&state_, metadata_.get(), crash_address_);
+  error_string_ = gwp_asan::ErrorToString(error_);
+  responsible_allocation_ = __gwp_asan_get_metadata(&state_, metadata_.get(), crash_address_);
+}
+
+bool GwpAsanCrashData::CrashIsMine() const {
+  return is_gwp_asan_responsible_;
+}
+
+void GwpAsanCrashData::DumpCause(log_t* log) const {
+  if (!CrashIsMine()) {
+    ALOGE("Internal Error: DumpCause() on a non-GWP-ASan crash.");
+    return;
+  }
+
+  if (error_ == gwp_asan::Error::UNKNOWN) {
+    _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: Unknown error occurred at 0x%" PRIxPTR ".\n",
+         crash_address_);
+    return;
+  }
+
+  if (!responsible_allocation_) {
+    _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s at 0x%" PRIxPTR ".\n", error_string_,
+         crash_address_);
+    return;
+  }
+
+  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;
+
+  if (crash_address_ < alloc_address) {
+    // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
+    location_str = "left of";
+    diff = alloc_address - crash_address_;
+  } else if (crash_address_ - alloc_address < alloc_size) {
+    // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
+    location_str = "into";
+    diff = crash_address_ - alloc_address;
+  } else {
+    // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef, or
+    // Invalid Free, 47 bytes right of a 41-byte allocation at 0xdeadbeef.
+    location_str = "right of";
+    diff = crash_address_ - alloc_address;
+    if (error_ == gwp_asan::Error::BUFFER_OVERFLOW) {
+      diff -= alloc_size;
+    }
+  }
+
+  // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
+  const char* byte_suffix = "s";
+  if (diff == 1) {
+    byte_suffix = "";
+  }
+  _LOG(log, logtype::HEADER,
+       "Cause: [GWP-ASan]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
+       error_string_, diff, byte_suffix, location_str, alloc_size, alloc_address);
+}
+
+// Build a frame for symbolization using the maps from the provided unwinder.
+// The constructed frame contains just enough information to be used to
+// symbolize a GWP-ASan stack trace.
+static unwindstack::FrameData BuildFrame(unwindstack::Unwinder* unwinder, uintptr_t pc,
+                                         size_t frame_num) {
+  unwindstack::FrameData frame;
+  frame.num = frame_num;
+
+  unwindstack::Maps* maps = unwinder->GetMaps();
+  unwindstack::MapInfo* map_info = maps->Find(pc);
+  if (!map_info) {
+    frame.rel_pc = pc;
+    return frame;
+  }
+
+  unwindstack::Elf* elf =
+      map_info->GetElf(unwinder->GetProcessMemory(), unwindstack::Regs::CurrentArch());
+
+  uint64_t relative_pc = elf->GetRelPc(pc, map_info);
+
+  // Create registers just to get PC adjustment. Doesn't matter what they point
+  // to.
+  unwindstack::Regs* regs = unwindstack::Regs::CreateFromLocal();
+  uint64_t pc_adjustment = regs->GetPcAdjustment(relative_pc, elf);
+  relative_pc -= pc_adjustment;
+  // The debug PC may be different if the PC comes from the JIT.
+  uint64_t debug_pc = relative_pc;
+
+  // If we don't have a valid ELF file, check the JIT.
+  if (!elf->valid()) {
+    unwindstack::JitDebug jit_debug(unwinder->GetProcessMemory());
+    uint64_t jit_pc = pc - pc_adjustment;
+    unwindstack::Elf* jit_elf = jit_debug.GetElf(maps, jit_pc);
+    if (jit_elf != nullptr) {
+      debug_pc = jit_pc;
+      elf = jit_elf;
+    }
+  }
+
+  // Copy all the things we need into the frame for symbolization.
+  frame.rel_pc = relative_pc;
+  frame.pc = pc - pc_adjustment;
+  frame.map_name = map_info->name;
+  frame.map_elf_start_offset = map_info->elf_start_offset;
+  frame.map_exact_offset = map_info->offset;
+  frame.map_start = map_info->start;
+  frame.map_end = map_info->end;
+  frame.map_flags = map_info->flags;
+  frame.map_load_bias = elf->GetLoadBias();
+
+  if (!elf->GetFunctionName(relative_pc, &frame.function_name, &frame.function_offset)) {
+    frame.function_name = "";
+    frame.function_offset = 0;
+  }
+  return frame;
+}
+
+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_)) {
+    return false;
+  }
+  return true;
+}
+
+void GwpAsanCrashData::DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
+  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]);
+  size_t num_frames =
+      __gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+
+  if (thread_id == gwp_asan::kInvalidThreadID) {
+    _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread <unknown>:\n");
+  } else {
+    _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %" PRIu64 ":\n", thread_id);
+  }
+
+  unwinder->SetDisplayBuildID(true);
+  for (size_t i = 0; i < num_frames; ++i) {
+    unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
+    _LOG(log, logtype::BACKTRACE, "    %s\n", unwinder->FormatFrame(frame_data).c_str());
+  }
+}
+
+bool GwpAsanCrashData::HasAllocationTrace() const {
+  assert(CrashIsMine() && "HasAllocationTrace(): Crash is not mine!");
+  return responsible_allocation_ != nullptr;
+}
+
+void GwpAsanCrashData::DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
+  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]);
+  size_t num_frames =
+      __gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+
+  if (thread_id == gwp_asan::kInvalidThreadID) {
+    _LOG(log, logtype::BACKTRACE, "\nallocated by thread <unknown>:\n");
+  } else {
+    _LOG(log, logtype::BACKTRACE, "\nallocated by thread %" PRIu64 ":\n", thread_id);
+  }
+
+  unwinder->SetDisplayBuildID(true);
+  for (size_t i = 0; i < num_frames; ++i) {
+    unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], 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
new file mode 100644
index 0000000..aef4c62
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <log/log.h>
+#include <unwindstack/Memory.h>
+
+#include "gwp_asan/common.h"
+#include "types.h"
+#include "utility.h"
+
+class GwpAsanCrashData {
+ public:
+  GwpAsanCrashData() = delete;
+  ~GwpAsanCrashData() = default;
+
+  // Construct the crash data object. Takes a handle to the object that can
+  // supply the memory of the dead process, and pointers to the GWP-ASan state
+  // and metadata regions within that process. Also takes the thread information
+  // of the crashed process. If the process didn't crash via SEGV, GWP-ASan may
+  // still be responsible, as it terminates when it detects an internal error
+  // (double free, invalid free). In these cases, we will retrieve the fault
+  // address from the GWP-ASan allocator's state.
+  GwpAsanCrashData(unwindstack::Memory* process_memory, uintptr_t gwp_asan_state_ptr,
+                   uintptr_t gwp_asan_metadata_ptr, const ThreadInfo& thread_info);
+
+  // Is GWP-ASan responsible for this crash.
+  bool CrashIsMine() const;
+
+  // Returns the fault address. The fault address may be the same as provided
+  // during construction, or it may have been retrieved from GWP-ASan's internal
+  // allocator crash state.
+  uintptr_t GetFaultAddress() const;
+
+  // Dump the GWP-ASan stringified cause of this crash. May only be called if
+  // CrashIsMine() returns true.
+  void DumpCause(log_t* log) const;
+
+  // Returns whether this crash has a deallocation trace. May only be called if
+  // CrashIsMine() returns true.
+  bool HasDeallocationTrace() const;
+
+  // Dump the GWP-ASan deallocation trace for this crash. May only be called if
+  // HasDeallocationTrace() returns true.
+  void DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+  // Returns whether this crash has a allocation trace. May only be called if
+  // CrashIsMine() returns true.
+  bool HasAllocationTrace() const;
+
+  // Dump the GWP-ASan allocation trace for this crash. May only be called if
+  // HasAllocationTrace() returns true.
+  void DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+ protected:
+  // Is GWP-ASan responsible for this crash.
+  bool is_gwp_asan_responsible_ = false;
+
+  // Thread ID of the crash.
+  size_t thread_id_;
+
+  // The type of error that GWP-ASan caused (and the stringified version),
+  // Undefined if GWP-ASan isn't responsible for the crash.
+  gwp_asan::Error error_;
+  const char* error_string_;
+
+  // Pointer to the crash address. Holds the internal crash address if it
+  // exists, otherwise the address provided at construction.
+  uintptr_t crash_address_ = 0u;
+
+  // Pointer to the metadata for the responsible allocation, nullptr if it
+  // doesn't exist.
+  const gwp_asan::AllocationMetadata* responsible_allocation_ = nullptr;
+
+  // Internal state.
+  gwp_asan::AllocatorState state_;
+  std::unique_ptr<const gwp_asan::AllocationMetadata> metadata_;
+};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 7133f77..291d994 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -55,6 +55,7 @@
 void engrave_tombstone(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
                        const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
                        uint64_t abort_msg_address, OpenFilesList* open_files,
-                       std::string* amfd_data);
+                       std::string* amfd_data, uintptr_t gwp_asan_state,
+                       uintptr_t gwp_asan_metadata);
 
 #endif  // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index b33adf31..eed95bc 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -23,6 +23,7 @@
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include "libdebuggerd/utility.h"
@@ -31,8 +32,13 @@
 #include "host_signal_fixup.h"
 #include "log_fake.h"
 
+// Include tombstone.cpp to define log_tag before GWP-ASan includes log.
 #include "tombstone.cpp"
 
+#include "gwp_asan.cpp"
+
+using ::testing::MatchesRegex;
+
 class TombstoneTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
@@ -359,3 +365,131 @@
   dump_timestamp(&log_, 0);
   ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
 }
+
+class GwpAsanCrashDataTest : public GwpAsanCrashData {
+public:
+  GwpAsanCrashDataTest(
+      gwp_asan::Error error,
+      const gwp_asan::AllocationMetadata *responsible_allocation) :
+      GwpAsanCrashData(nullptr, 0u, 0u, ThreadInfo{}) {
+    is_gwp_asan_responsible_ = true;
+    error_ = error;
+    responsible_allocation_ = responsible_allocation;
+    error_string_ = gwp_asan::ErrorToString(error_);
+  }
+
+  void SetCrashAddress(uintptr_t crash_address) {
+    crash_address_ = crash_address;
+  }
+};
+
+TEST_F(TombstoneTest, gwp_asan_cause_uaf_exact) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::USE_AFTER_FREE, &meta);
+  crash_data.SetCrashAddress(0x1000);
+
+  crash_data.DumpCause(&log_);
+  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"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_double_free) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::DOUBLE_FREE, &meta);
+  crash_data.SetCrashAddress(0x1000);
+
+  crash_data.DumpCause(&log_);
+  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"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_overflow) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_OVERFLOW, &meta);
+  crash_data.SetCrashAddress(0x1025);
+
+  crash_data.DumpCause(&log_);
+  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\\]: Buffer Overflow, 5 bytes right of a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_underflow) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_UNDERFLOW, &meta);
+  crash_data.SetCrashAddress(0xffe);
+
+  crash_data.DumpCause(&log_);
+  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\\]: Buffer Underflow, 2 bytes left of a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_inside) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
+  crash_data.SetCrashAddress(0x1001);
+
+  crash_data.DumpCause(&log_);
+  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\\]: Invalid \\(Wild\\) Free, 1 byte into a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_outside) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.Size = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
+  crash_data.SetCrashAddress(0x1021);
+
+  crash_data.DumpCause(&log_);
+  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\\]: Invalid \\(Wild\\) Free, 33 bytes right of a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 4e7f35c..fd52e81 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -53,9 +53,13 @@
 #include <unwindstack/Unwinder.h>
 
 #include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/gwp_asan.h"
 #include "libdebuggerd/open_files_list.h"
 #include "libdebuggerd/utility.h"
 
+#include "gwp_asan/common.h"
+#include "gwp_asan/crash_handler.h"
+
 using android::base::GetBoolProperty;
 using android::base::GetProperty;
 using android::base::StringPrintf;
@@ -372,7 +376,8 @@
 }
 
 static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const ThreadInfo& thread_info,
-                        uint64_t abort_msg_address, bool primary_thread) {
+                        uint64_t abort_msg_address, bool primary_thread,
+                        const GwpAsanCrashData& gwp_asan_crash_data) {
   log->current_tid = thread_info.tid;
   if (!primary_thread) {
     _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
@@ -381,7 +386,13 @@
 
   if (thread_info.siginfo) {
     dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
-    dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get());
+  }
+
+  if (primary_thread && gwp_asan_crash_data.CrashIsMine()) {
+    gwp_asan_crash_data.DumpCause(log);
+  } else if (thread_info.siginfo) {
+    dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(),
+                        thread_info.registers.get());
   }
 
   if (primary_thread) {
@@ -402,6 +413,14 @@
   }
 
   if (primary_thread) {
+    if (gwp_asan_crash_data.HasDeallocationTrace()) {
+      gwp_asan_crash_data.DumpDeallocationTrace(log, unwinder);
+    }
+
+    if (gwp_asan_crash_data.HasAllocationTrace()) {
+      gwp_asan_crash_data.DumpAllocationTrace(log, unwinder);
+    }
+
     unwindstack::Maps* maps = unwinder->GetMaps();
     dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
                          thread_info.registers.get());
@@ -583,13 +602,14 @@
   }
 
   engrave_tombstone(unique_fd(dup(tombstone_fd)), &unwinder, threads, tid, abort_msg_address,
-                    nullptr, nullptr);
+                    nullptr, nullptr, 0u, 0u);
 }
 
 void engrave_tombstone(unique_fd output_fd, unwindstack::Unwinder* unwinder,
                        const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
                        uint64_t abort_msg_address, OpenFilesList* open_files,
-                       std::string* amfd_data) {
+                       std::string* amfd_data, uintptr_t gwp_asan_state_ptr,
+                       uintptr_t gwp_asan_metadata_ptr) {
   // don't copy log messages to tombstone unless this is a dev device
   bool want_logs = android::base::GetBoolProperty("ro.debuggable", false);
 
@@ -607,7 +627,13 @@
   if (it == threads.end()) {
     LOG(FATAL) << "failed to find target thread";
   }
-  dump_thread(&log, unwinder, it->second, abort_msg_address, true);
+
+  GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(),
+                                       gwp_asan_state_ptr,
+                                       gwp_asan_metadata_ptr, it->second);
+
+  dump_thread(&log, unwinder, it->second, abort_msg_address, true,
+              gwp_asan_crash_data);
 
   if (want_logs) {
     dump_logs(&log, it->second.pid, 50);
@@ -618,7 +644,7 @@
       continue;
     }
 
-    dump_thread(&log, unwinder, thread_info, 0, false);
+    dump_thread(&log, unwinder, thread_info, 0, false, gwp_asan_crash_data);
   }
 
   if (open_files) {