Merge "Revert^2 "Exec_start derive_classpath on post-fs-data.""
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index fb274ec..7b6f6c0 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -238,6 +238,7 @@
         "gwp_asan_crash_handler",
         "libscudo",
         "libtombstone_proto",
+        "libprocinfo",
         "libprotobuf-cpp-lite",
     ],
 
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index de37a5b..144faee 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -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>
 
@@ -98,6 +99,13 @@
   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,
@@ -397,6 +405,72 @@
 }
 #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([&params]() {
+    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));
@@ -428,12 +502,11 @@
 
   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
@@ -465,9 +538,8 @@
 
   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";
@@ -500,9 +572,8 @@
 
   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";
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
index 9750fc4..3ee309f 100644
--- a/debuggerd/libdebuggerd/gwp_asan.cpp
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "libdebuggerd/gwp_asan.h"
+#include "libdebuggerd/tombstone.h"
 #include "libdebuggerd/utility.h"
 
 #include "gwp_asan/common.h"
@@ -25,6 +26,8 @@
 #include <unwindstack/Regs.h>
 #include <unwindstack/Unwinder.h>
 
+#include "tombstone.pb.h"
+
 // Retrieve GWP-ASan state from `state_addr` inside the process at
 // `process_memory`. Place the state into `*state`.
 static bool retrieve_gwp_asan_state(unwindstack::Memory* process_memory, uintptr_t state_addr,
@@ -98,6 +101,67 @@
   return is_gwp_asan_responsible_;
 }
 
+constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
+
+void GwpAsanCrashData::AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const {
+  if (!CrashIsMine()) {
+    ALOGE("Internal Error: AddCauseProtos() on a non-GWP-ASan crash.");
+    return;
+  }
+
+  Cause* cause = tombstone->add_causes();
+  MemoryError* memory_error = cause->mutable_memory_error();
+  HeapObject* heap_object = memory_error->mutable_heap();
+
+  memory_error->set_tool(MemoryError_Tool_GWP_ASAN);
+  switch (error_) {
+    case gwp_asan::Error::USE_AFTER_FREE:
+      memory_error->set_type(MemoryError_Type_USE_AFTER_FREE);
+      break;
+    case gwp_asan::Error::DOUBLE_FREE:
+      memory_error->set_type(MemoryError_Type_DOUBLE_FREE);
+      break;
+    case gwp_asan::Error::INVALID_FREE:
+      memory_error->set_type(MemoryError_Type_INVALID_FREE);
+      break;
+    case gwp_asan::Error::BUFFER_OVERFLOW:
+      memory_error->set_type(MemoryError_Type_BUFFER_OVERFLOW);
+      break;
+    case gwp_asan::Error::BUFFER_UNDERFLOW:
+      memory_error->set_type(MemoryError_Type_BUFFER_UNDERFLOW);
+      break;
+    default:
+      memory_error->set_type(MemoryError_Type_UNKNOWN);
+      break;
+  }
+
+  heap_object->set_address(__gwp_asan_get_allocation_address(responsible_allocation_));
+  heap_object->set_size(__gwp_asan_get_allocation_size(responsible_allocation_));
+  unwinder->SetDisplayBuildID(true);
+
+  std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
+
+  heap_object->set_allocation_tid(__gwp_asan_get_allocation_thread_id(responsible_allocation_));
+  size_t num_frames =
+      __gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+  for (size_t i = 0; i != num_frames; ++i) {
+    unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
+    BacktraceFrame* f = heap_object->add_allocation_backtrace();
+    fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
+  }
+
+  heap_object->set_deallocation_tid(__gwp_asan_get_deallocation_thread_id(responsible_allocation_));
+  num_frames =
+      __gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+  for (size_t i = 0; i != num_frames; ++i) {
+    unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
+    BacktraceFrame* f = heap_object->add_deallocation_backtrace();
+    fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
+  }
+
+  set_human_readable_cause(cause, crash_address_);
+}
+
 void GwpAsanCrashData::DumpCause(log_t* log) const {
   if (!CrashIsMine()) {
     ALOGE("Internal Error: DumpCause() on a non-GWP-ASan crash.");
@@ -119,13 +183,6 @@
   uintptr_t alloc_address = __gwp_asan_get_allocation_address(responsible_allocation_);
   size_t alloc_size = __gwp_asan_get_allocation_size(responsible_allocation_);
 
-  if (crash_address_ == alloc_address) {
-    // Use After Free on a 41-byte allocation at 0xdeadbeef.
-    _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s on a %zu-byte allocation at 0x%" PRIxPTR "\n",
-         error_string_, alloc_size, alloc_address);
-    return;
-  }
-
   uintptr_t diff;
   const char* location_str;
 
@@ -157,8 +214,6 @@
        error_string_, diff, byte_suffix, location_str, alloc_size, alloc_address);
 }
 
-constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
-
 bool GwpAsanCrashData::HasDeallocationTrace() const {
   assert(CrashIsMine() && "HasDeallocationTrace(): Crash is not mine!");
   if (!responsible_allocation_ || !__gwp_asan_is_deallocated(responsible_allocation_)) {
@@ -171,7 +226,7 @@
   assert(HasDeallocationTrace() && "DumpDeallocationTrace(): No dealloc trace!");
   uint64_t thread_id = __gwp_asan_get_deallocation_thread_id(responsible_allocation_);
 
-  std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]);
+  std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
   size_t num_frames =
       __gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
 
@@ -183,7 +238,7 @@
 
   unwinder->SetDisplayBuildID(true);
   for (size_t i = 0; i < num_frames; ++i) {
-    unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames.get()[i]);
+    unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
     frame_data.num = i;
     _LOG(log, logtype::BACKTRACE, "    %s\n", unwinder->FormatFrame(frame_data).c_str());
   }
@@ -198,7 +253,7 @@
   assert(HasAllocationTrace() && "DumpAllocationTrace(): No dealloc trace!");
   uint64_t thread_id = __gwp_asan_get_allocation_thread_id(responsible_allocation_);
 
-  std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]);
+  std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
   size_t num_frames =
       __gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
 
@@ -210,7 +265,7 @@
 
   unwinder->SetDisplayBuildID(true);
   for (size_t i = 0; i < num_frames; ++i) {
-    unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames.get()[i]);
+    unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
     frame_data.num = i;
     _LOG(log, logtype::BACKTRACE, "    %s\n", unwinder->FormatFrame(frame_data).c_str());
   }
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
index 6c88733..f9c2481 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -26,6 +26,9 @@
 #include "types.h"
 #include "utility.h"
 
+class Cause;
+class Tombstone;
+
 class GwpAsanCrashData {
  public:
   GwpAsanCrashData() = delete;
@@ -69,6 +72,8 @@
   // HasAllocationTrace() returns true.
   void DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
 
+  void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
+
  protected:
   // Is GWP-ASan responsible for this crash.
   bool is_gwp_asan_responsible_ = false;
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
index 4d00ece..c3b95d6 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -23,6 +23,9 @@
 
 #include "scudo/interface.h"
 
+class Cause;
+class Tombstone;
+
 class ScudoCrashData {
  public:
   ScudoCrashData() = delete;
@@ -32,6 +35,7 @@
   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 @@
 
   void DumpReport(const scudo_error_report* report, log_t* log,
                   unwindstack::Unwinder* unwinder) const;
+
+  void FillInCause(Cause* cause, const scudo_error_report* report,
+                   unwindstack::Unwinder* unwinder) const;
 };
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index bf2cbb3..2331f1e 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -31,9 +31,13 @@
 #include "types.h"
 
 // Forward declarations
+class BacktraceFrame;
+class Cause;
 class Tombstone;
 
 namespace unwindstack {
+struct FrameData;
+class Maps;
 class Unwinder;
 }
 
@@ -64,4 +68,8 @@
     const Tombstone& tombstone,
     std::function<void(const std::string& line, bool should_log)> callback);
 
+void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame,
+                             unwindstack::Maps* maps);
+void set_human_readable_cause(Cause* cause, uint64_t fault_addr);
+
 #endif  // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index d71b76f..c490fb1 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -81,7 +81,7 @@
 
 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 @@
 const char* get_signame(const siginfo_t*);
 const char* get_sigcode(const siginfo_t*);
 
+// Number of bytes per MTE granule.
+constexpr size_t kTagGranuleSize = 16;
+
 #endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index 1c3437f..f4690ba 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -15,13 +15,16 @@
  */
 
 #include "libdebuggerd/scudo.h"
-#include "libdebuggerd/gwp_asan.h"
+#include "libdebuggerd/tombstone.h"
 
 #include "unwindstack/Memory.h"
 #include "unwindstack/Unwinder.h"
 
+#include <android-base/macros.h>
 #include <bionic/macros.h>
 
+#include "tombstone.pb.h"
+
 std::unique_ptr<char[]> AllocAndReadFully(unwindstack::Memory* process_memory, uint64_t addr,
                                           size_t size) {
   auto buf = std::make_unique<char[]>(size);
@@ -31,8 +34,6 @@
   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 @@
   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 @@
   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 @@
   if (report->deallocation_trace[0]) {
     _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %u:\n", report->deallocation_tid);
     unwinder->SetDisplayBuildID(true);
-    for (size_t i = 0; i < 64 && report->deallocation_trace[i]; ++i) {
+    for (size_t i = 0; i < arraysize(report->deallocation_trace) && report->deallocation_trace[i];
+         ++i) {
       unwindstack::FrameData frame_data =
           unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);
       frame_data.num = i;
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
index f16f578..5be145a 100644
--- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -73,34 +73,14 @@
 "    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 @@
   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 @@
     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 @@
   const char* expected_dump = \
 "\nmemory near r4:\n"
 #if defined(__LP64__)
-"    0000000010000f40 ---------------- ----------------  ................\n"
-"    0000000010000f50 ---------------- ----------------  ................\n"
-"    0000000010000f60 ---------------- ----------------  ................\n"
-"    0000000010000f70 ---------------- ----------------  ................\n"
-"    0000000010000f80 ---------------- ----------------  ................\n"
-"    0000000010000f90 ---------------- ----------------  ................\n"
-"    0000000010000fa0 ---------------- ----------------  ................\n"
-"    0000000010000fb0 ---------------- ----------------  ................\n"
-"    0000000010000fc0 ---------------- ----------------  ................\n"
-"    0000000010000fd0 ---------------- ----------------  ................\n"
-"    0000000010000fe0 ---------------- ----------------  ................\n"
-"    0000000010000ff0 ---------------- ----------------  ................\n"
 "    0000000010001000 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................\n"
-"    0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n"
-"    0000000010001020 ---------------- ----------------  ................\n"
-"    0000000010001030 ---------------- ----------------  ................\n";
+"    0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n";
 #else
-"    10000f40 -------- -------- -------- --------  ................\n"
-"    10000f50 -------- -------- -------- --------  ................\n"
-"    10000f60 -------- -------- -------- --------  ................\n"
-"    10000f70 -------- -------- -------- --------  ................\n"
-"    10000f80 -------- -------- -------- --------  ................\n"
-"    10000f90 -------- -------- -------- --------  ................\n"
-"    10000fa0 -------- -------- -------- --------  ................\n"
-"    10000fb0 -------- -------- -------- --------  ................\n"
-"    10000fc0 -------- -------- -------- --------  ................\n"
-"    10000fd0 -------- -------- -------- --------  ................\n"
-"    10000fe0 -------- -------- -------- --------  ................\n"
-"    10000ff0 -------- -------- -------- --------  ................\n"
 "    10001000 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc  ................\n"
-"    10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................\n"
-"    10001020 -------- -------- -------- --------  ................\n"
-"    10001030 -------- -------- -------- --------  ................\n";
+"    10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................\n";
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 7fe8f82..79ac122 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -388,9 +388,8 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   std::string tombstone_contents;
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  ASSERT_THAT(tombstone_contents,
-              MatchesRegex("Cause: \\[GWP-ASan\\]: Use After Free on a 32-byte "
-                           "allocation at 0x[a-fA-F0-9]+\n"));
+  ASSERT_THAT(tombstone_contents, MatchesRegex("Cause: \\[GWP-ASan\\]: Use After Free, 0 bytes "
+                                               "into a 32-byte allocation at 0x[a-fA-F0-9]+\n"));
 }
 
 TEST_F(TombstoneTest, gwp_asan_cause_double_free) {
@@ -405,9 +404,8 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   std::string tombstone_contents;
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  ASSERT_THAT(tombstone_contents,
-              MatchesRegex("Cause: \\[GWP-ASan\\]: Double Free on a 32-byte "
-                           "allocation at 0x[a-fA-F0-9]+\n"));
+  ASSERT_THAT(tombstone_contents, MatchesRegex("Cause: \\[GWP-ASan\\]: Double Free, 0 bytes into a "
+                                               "32-byte allocation at 0x[a-fA-F0-9]+\n"));
 }
 
 TEST_F(TombstoneTest, gwp_asan_cause_overflow) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 23ca070..3444e29 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "DEBUG"
 
 #include "libdebuggerd/tombstone.h"
+#include "libdebuggerd/gwp_asan.h"
+#include "libdebuggerd/scudo.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -29,10 +31,12 @@
 #include <time.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 
 #include <async_safe/log.h>
 
+#include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -45,6 +49,7 @@
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
 
+#include <procinfo/process.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
@@ -106,32 +111,120 @@
   return {};
 }
 
-static void dump_probable_cause(Tombstone* tombstone, const siginfo_t* si, unwindstack::Maps* maps,
-                                unwindstack::Regs* regs) {
+void set_human_readable_cause(Cause* cause, uint64_t fault_addr) {
+  if (!cause->has_memory_error() || !cause->memory_error().has_heap()) {
+    return;
+  }
+
+  const MemoryError& memory_error = cause->memory_error();
+  const HeapObject& heap_object = memory_error.heap();
+
+  const char *tool_str;
+  switch (memory_error.tool()) {
+    case MemoryError_Tool_GWP_ASAN:
+      tool_str = "GWP-ASan";
+      break;
+    case MemoryError_Tool_SCUDO:
+      tool_str = "MTE";
+      break;
+    default:
+      tool_str = "Unknown";
+      break;
+  }
+
+  const char *error_type_str;
+  switch (memory_error.type()) {
+    case MemoryError_Type_USE_AFTER_FREE:
+      error_type_str = "Use After Free";
+      break;
+    case MemoryError_Type_DOUBLE_FREE:
+      error_type_str = "Double Free";
+      break;
+    case MemoryError_Type_INVALID_FREE:
+      error_type_str = "Invalid (Wild) Free";
+      break;
+    case MemoryError_Type_BUFFER_OVERFLOW:
+      error_type_str = "Buffer Overflow";
+      break;
+    case MemoryError_Type_BUFFER_UNDERFLOW:
+      error_type_str = "Buffer Underflow";
+      break;
+    default:
+      cause->set_human_readable(
+          StringPrintf("[%s]: Unknown error occurred at 0x%" PRIx64 ".", tool_str, fault_addr));
+      return;
+  }
+
+  uint64_t diff;
+  const char* location_str;
+
+  if (fault_addr < heap_object.address()) {
+    // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
+    location_str = "left of";
+    diff = heap_object.address() - fault_addr;
+  } else if (fault_addr - heap_object.address() < heap_object.size()) {
+    // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
+    location_str = "into";
+    diff = fault_addr - heap_object.address();
+  } else {
+    // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef.
+    location_str = "right of";
+    diff = fault_addr - heap_object.address() - heap_object.size();
+  }
+
+  // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
+  const char* byte_suffix = "s";
+  if (diff == 1) {
+    byte_suffix = "";
+  }
+
+  cause->set_human_readable(StringPrintf(
+      "[%s]: %s, %" PRIu64 " byte%s %s a %" PRIu64 "-byte allocation at 0x%" PRIx64, tool_str,
+      error_type_str, diff, byte_suffix, location_str, heap_object.size(), heap_object.address()));
+}
+
+static void dump_probable_cause(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+                                const ProcessInfo& process_info, const ThreadInfo& main_thread) {
+  ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
+  if (scudo_crash_data.CrashIsMine()) {
+    scudo_crash_data.AddCauseProtos(tombstone, unwinder);
+    return;
+  }
+
+  GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(), process_info,
+                                       main_thread);
+  if (gwp_asan_crash_data.CrashIsMine()) {
+    gwp_asan_crash_data.AddCauseProtos(tombstone, unwinder);
+    return;
+  }
+
+  const siginfo *si = main_thread.siginfo;
+  auto fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
+  unwindstack::Maps* maps = unwinder->GetMaps();
+
   std::optional<std::string> cause;
   if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
-    if (si->si_addr < reinterpret_cast<void*>(4096)) {
+    if (fault_addr < 4096) {
       cause = "null pointer dereference";
-    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0ffc)) {
+    } else if (fault_addr == 0xffff0ffc) {
       cause = "call to kuser_helper_version";
-    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fe0)) {
+    } else if (fault_addr == 0xffff0fe0) {
       cause = "call to kuser_get_tls";
-    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fc0)) {
+    } else if (fault_addr == 0xffff0fc0) {
       cause = "call to kuser_cmpxchg";
-    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fa0)) {
+    } else if (fault_addr == 0xffff0fa0) {
       cause = "call to kuser_memory_barrier";
-    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) {
+    } else if (fault_addr == 0xffff0f60) {
       cause = "call to kuser_cmpxchg64";
     } else {
-      cause = get_stack_overflow_cause(reinterpret_cast<uint64_t>(si->si_addr), regs->sp(), maps);
+      cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps);
     }
   } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
-    uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
     unwindstack::MapInfo* map_info = maps->Find(fault_addr);
     if (map_info != nullptr && map_info->flags == PROT_EXEC) {
       cause = "execute-only (no-read) memory access error; likely due to data in .text.";
     } else {
-      cause = get_stack_overflow_cause(fault_addr, regs->sp(), maps);
+      cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps);
     }
   } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
     cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
@@ -139,7 +232,8 @@
   }
 
   if (cause) {
-    tombstone->mutable_cause()->set_human_readable(*cause);
+    Cause *cause_proto = tombstone->add_causes();
+    cause_proto->set_human_readable(*cause);
   }
 }
 
@@ -205,12 +299,49 @@
   }
 }
 
+void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame,
+                             unwindstack::Maps* maps) {
+  f->set_rel_pc(frame.rel_pc);
+  f->set_pc(frame.pc);
+  f->set_sp(frame.sp);
+
+  if (!frame.function_name.empty()) {
+    // TODO: Should this happen here, or on the display side?
+    char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr);
+    if (demangled_name) {
+      f->set_function_name(demangled_name);
+      free(demangled_name);
+    } else {
+      f->set_function_name(frame.function_name);
+    }
+  }
+
+  f->set_function_offset(frame.function_offset);
+
+  if (frame.map_start == frame.map_end) {
+    // No valid map associated with this frame.
+    f->set_file_name("<unknown>");
+  } else if (!frame.map_name.empty()) {
+    f->set_file_name(frame.map_name);
+  } else {
+    f->set_file_name(StringPrintf("<anonymous:%" PRIx64 ">", frame.map_start));
+  }
+
+  f->set_file_map_offset(frame.map_elf_start_offset);
+
+  unwindstack::MapInfo* map_info = maps->Find(frame.map_start);
+  if (map_info) {
+    f->set_build_id(map_info->GetPrintableBuildID());
+  }
+}
+
 static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
                         const ThreadInfo& thread_info, bool memory_dump = false) {
   Thread thread;
 
   thread.set_id(thread_info.tid);
   thread.set_name(thread_info.thread_name);
+  thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
 
   unwindstack::Maps* maps = unwinder->GetMaps();
   unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
@@ -225,20 +356,19 @@
         if (memory_dump) {
           MemoryDump dump;
 
-          char buf[256];
-          size_t start_offset = 0;
-          ssize_t bytes = dump_memory(buf, sizeof(buf), &start_offset, &value, memory);
-          if (bytes == -1) {
-            return;
-          }
-
           dump.set_register_name(name);
-
           unwindstack::MapInfo* map_info = maps->Find(untag_address(value));
           if (map_info) {
             dump.set_mapping_name(map_info->name);
           }
 
+          char buf[256];
+          uint8_t tags[256 / kTagGranuleSize];
+          size_t start_offset = 0;
+          ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
+          if (bytes == -1) {
+            return;
+          }
           dump.set_begin_address(value);
 
           if (start_offset + bytes > sizeof(buf)) {
@@ -246,7 +376,8 @@
                              start_offset, bytes);
           }
 
-          dump.set_memory(buf, start_offset + bytes);
+          dump.set_memory(buf, bytes);
+          dump.set_tags(tags, bytes / kTagGranuleSize);
 
           *thread.add_memory_dump() = std::move(dump);
         }
@@ -267,39 +398,7 @@
     unwinder->SetDisplayBuildID(true);
     for (const auto& frame : unwinder->frames()) {
       BacktraceFrame* f = thread.add_current_backtrace();
-      f->set_rel_pc(frame.rel_pc);
-      f->set_pc(frame.pc);
-      f->set_sp(frame.sp);
-
-      if (!frame.function_name.empty()) {
-        // TODO: Should this happen here, or on the display side?
-        char* demangled_name =
-            __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr);
-        if (demangled_name) {
-          f->set_function_name(demangled_name);
-          free(demangled_name);
-        } else {
-          f->set_function_name(frame.function_name);
-        }
-      }
-
-      f->set_function_offset(frame.function_offset);
-
-      if (frame.map_start == frame.map_end) {
-        // No valid map associated with this frame.
-        f->set_file_name("<unknown>");
-      } else if (!frame.map_name.empty()) {
-        f->set_file_name(frame.map_name);
-      } else {
-        f->set_file_name(StringPrintf("<anonymous:%" PRIx64 ">", frame.map_start));
-      }
-
-      f->set_file_map_offset(frame.map_elf_start_offset);
-
-      unwindstack::MapInfo* map_info = maps->Find(frame.map_start);
-      if (map_info) {
-        f->set_build_id(map_info->GetPrintableBuildID());
-      }
+      fill_in_backtrace_frame(f, frame, maps);
     }
   }
 
@@ -423,6 +522,14 @@
   dump_log_file(tombstone, "main", pid);
 }
 
+static std::optional<uint64_t> read_uptime_secs() {
+  std::string uptime;
+  if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
+    return {};
+  }
+  return strtoll(uptime.c_str(), nullptr, 10);
+}
+
 void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
                              const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
                              const ProcessInfo& process_info, const OpenFilesList* open_files) {
@@ -433,6 +540,22 @@
   result.set_revision(android::base::GetProperty("ro.revision", "unknown"));
   result.set_timestamp(get_timestamp());
 
+  std::optional<uint64_t> system_uptime = read_uptime_secs();
+  if (system_uptime) {
+    android::procinfo::ProcessInfo proc_info;
+    std::string error;
+    if (android::procinfo::GetProcessInfo(target_thread, &proc_info, &error)) {
+      uint64_t starttime = proc_info.starttime / sysconf(_SC_CLK_TCK);
+      result.set_process_uptime(*system_uptime - starttime);
+    } else {
+      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read process info: %s",
+                            error.c_str());
+    }
+  } else {
+    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read /proc/uptime: %s",
+                          strerror(errno));
+  }
+
   const ThreadInfo& main_thread = threads.at(target_thread);
   result.set_pid(main_thread.pid);
   result.set_tid(main_thread.tid);
@@ -458,7 +581,7 @@
 
   if (process_info.has_fault_address) {
     sig.set_has_fault_address(true);
-    sig.set_fault_address(process_info.untagged_fault_address);
+    sig.set_fault_address(process_info.maybe_tagged_fault_address);
   }
 
   *result.mutable_signal_info() = sig;
@@ -473,8 +596,7 @@
     }
   }
 
-  dump_probable_cause(&result, main_thread.siginfo, unwinder->GetMaps(),
-                      main_thread.registers.get());
+  dump_probable_cause(&result, unwinder, process_info, main_thread);
 
   dump_mappings(&result, unwinder);
 
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 187379d..00ca7c1 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -74,6 +74,9 @@
   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 @@
   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& 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 @@
         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 @@
 
   print_thread_registers(callback, tombstone, thread, true);
   print_thread_backtrace(callback, tombstone, thread, true);
+
+  if (tombstone.causes_size() > 1) {
+    CBS("");
+    CBS("Note: multiple potential causes for this crash were detected, listing them in decreasing "
+        "order of probability.");
+  }
+
+  for (const Cause& cause : tombstone.causes()) {
+    if (tombstone.causes_size() > 1) {
+      CBS("");
+      CBS("Cause: %s", cause.human_readable().c_str());
+    }
+
+    if (cause.has_memory_error() && cause.memory_error().has_heap()) {
+      const HeapObject& heap_object = cause.memory_error().heap();
+
+      if (heap_object.deallocation_backtrace_size() != 0) {
+        CBS("");
+        CBS("deallocated by thread %" PRIu64 ":", heap_object.deallocation_tid());
+        print_backtrace(callback, tombstone, heap_object.deallocation_backtrace(), false);
+      }
+
+      if (heap_object.allocation_backtrace_size() != 0) {
+        CBS("");
+        CBS("allocated by thread %" PRIu64 ":", heap_object.allocation_tid());
+        print_backtrace(callback, tombstone, heap_object.allocation_backtrace(), false);
+      }
+    }
+  }
+
   print_thread_memory_dump(callback, tombstone, thread);
 
   CBS("");
@@ -313,6 +360,7 @@
   CBL("Revision: '%s'", tombstone.revision().c_str());
   CBL("ABI: '%s'", abi_string(tombstone));
   CBL("Timestamp: %s", tombstone.timestamp().c_str());
+  CBL("Process uptime: %ds", tombstone.process_uptime());
 
   // Process header
   const auto& threads = tombstone.threads();
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 6f13ed4..2c645b5 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -125,8 +125,9 @@
 
 #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 @@
     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 @@
     // 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 @@
     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 @@
   // 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());
   }
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index 2c7156b..dd15ff6 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -19,16 +19,19 @@
 
   string process_name = 9;
 
+  // Process uptime in seconds.
+  uint32 process_uptime = 20;
+
   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;
   repeated LogBuffer log_buffers = 18;
   repeated FD open_fds = 19;
 
-  reserved 20 to 999;
+  reserved 21 to 999;
 }
 
 enum Architecture {
@@ -57,10 +60,52 @@
   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 +121,9 @@
   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 +146,9 @@
   string mapping_name = 2;
   uint64 begin_address = 3;
   bytes memory = 4;
+  bytes tags = 5;
 
-  reserved 5 to 999;
+  reserved 6 to 999;
 }
 
 message MemoryMapping {
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 3c4269b..bf9ec90 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -127,6 +127,7 @@
         "-Wextra",
         "-Werror",
         "-Wvla",
+        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
     ],
     rtti: true,
 
@@ -206,6 +207,7 @@
         "-Wextra",
         "-Werror",
         "-Wunreachable-code",
+        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
     ],
 
     target: {
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b72f3fe..0a72812 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -392,13 +392,13 @@
 
     struct sockaddr_un addr = {.sun_family = AF_UNIX};
     strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
-    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+    if (connect(sock.get(), (struct sockaddr*)&addr, sizeof(addr)) < 0) {
         PLOG(ERROR) << "Couldn't connect to recovery";
         return false;
     }
     // Switch to recovery will not update the boot reason since it does not
     // require a reboot.
-    auto ret = write(sock, &msg_switch_to_recovery, sizeof(msg_switch_to_recovery));
+    auto ret = write(sock.get(), &msg_switch_to_recovery, sizeof(msg_switch_to_recovery));
     if (ret != sizeof(msg_switch_to_recovery)) {
         PLOG(ERROR) << "Couldn't write message to switch to recovery";
         return false;
diff --git a/fastboot/device/usb.cpp b/fastboot/device/usb.cpp
index 4bee7b2..4115a6d 100644
--- a/fastboot/device/usb.cpp
+++ b/fastboot/device/usb.cpp
@@ -82,7 +82,7 @@
     int orig_len = len;
     while (len > 0) {
         int write_len = std::min(USB_FFS_BULK_SIZE, len);
-        int n = write(h->bulk_in, buf, write_len);
+        int n = write(h->bulk_in.get(), buf, write_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_in.get(), n, strerror(errno));
             return -1;
@@ -103,7 +103,7 @@
     unsigned count = 0;
     while (len > 0) {
         int read_len = std::min(USB_FFS_BULK_SIZE, len);
-        int n = read(h->bulk_out, buf, read_len);
+        int n = read(h->bulk_out.get(), buf, read_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_out.get(), n, strerror(errno));
             return -1;
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 94efeea..783cd19 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -230,9 +230,9 @@
     fprintf(stderr, "(bootloader) %s\n", info.c_str());
 }
 
-static int64_t get_file_size(int fd) {
+static int64_t get_file_size(borrowed_fd fd) {
     struct stat sb;
-    if (fstat(fd, &sb) == -1) {
+    if (fstat(fd.get(), &sb) == -1) {
         die("could not get file size");
     }
     return sb.st_size;
@@ -657,12 +657,12 @@
     fprintf(stderr, "extracting %s (%" PRIu64 " MB) to disk...", entry_name,
             zip_entry.uncompressed_length / 1024 / 1024);
     double start = now();
-    int error = ExtractEntryToFile(zip, &zip_entry, fd);
+    int error = ExtractEntryToFile(zip, &zip_entry, fd.get());
     if (error != 0) {
         die("\nfailed to extract '%s': %s", entry_name, ErrorCodeString(error));
     }
 
-    if (lseek(fd, 0, SEEK_SET) != 0) {
+    if (lseek(fd.get(), 0, SEEK_SET) != 0) {
         die("\nlseek on extracted file '%s' failed: %s", entry_name, strerror(errno));
     }
 
@@ -902,17 +902,18 @@
         return false;
     }
 
-    if (sparse_file* s = sparse_file_import(fd, false, false)) {
+    if (sparse_file* s = sparse_file_import(fd.get(), false, false)) {
         buf->image_size = sparse_file_len(s, false, false);
         sparse_file_destroy(s);
     } else {
         buf->image_size = sz;
     }
 
-    lseek(fd, 0, SEEK_SET);
+    lseek(fd.get(), 0, SEEK_SET);
     int64_t limit = get_sparse_limit(sz);
+    buf->fd = std::move(fd);
     if (limit) {
-        sparse_file** s = load_sparse_files(fd, limit);
+        sparse_file** s = load_sparse_files(buf->fd.get(), limit);
         if (s == nullptr) {
             return false;
         }
@@ -921,7 +922,6 @@
     } else {
         buf->type = FB_BUFFER_FD;
         buf->data = nullptr;
-        buf->fd = std::move(fd);
         buf->sz = sz;
     }
 
@@ -936,7 +936,7 @@
     }
 
     struct stat s;
-    if (fstat(fd, &s)) {
+    if (fstat(fd.get(), &s)) {
         return false;
     }
     if (!S_ISREG(s.st_mode)) {
@@ -995,7 +995,7 @@
         die("Failed writing to modified vbmeta");
     }
     buf->fd = std::move(fd);
-    lseek(fd, 0, SEEK_SET);
+    lseek(buf->fd.get(), 0, SEEK_SET);
 }
 
 static bool has_vbmeta_partition() {
@@ -1057,13 +1057,13 @@
     if (!android::base::WriteStringToFd(data, fd)) {
         die("Failed writing to modified boot");
     }
-    lseek(fd, partition_size - AVB_FOOTER_SIZE, SEEK_SET);
+    lseek(fd.get(), partition_size - AVB_FOOTER_SIZE, SEEK_SET);
     if (!android::base::WriteStringToFd(data.substr(footer_offset), fd)) {
         die("Failed copying AVB footer in boot");
     }
     buf->fd = std::move(fd);
     buf->sz = partition_size;
-    lseek(fd, 0, SEEK_SET);
+    lseek(buf->fd.get(), 0, SEEK_SET);
 }
 
 static void flash_buf(const std::string& partition, struct fastboot_buffer *buf)
@@ -1538,7 +1538,7 @@
 }
 
 void FlashAllTool::UpdateSuperPartition() {
-    int fd = source_.OpenFile("super_empty.img");
+    unique_fd fd = source_.OpenFile("super_empty.img");
     if (fd < 0) {
         return;
     }
@@ -2197,7 +2197,7 @@
             if (!load_buf(filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
                 die("cannot load '%s'", filename.c_str());
             }
-            fb->Download(filename, buf.fd, buf.sz);
+            fb->Download(filename, buf.fd.get(), buf.sz);
         } else if (command == "get_staged") {
             std::string filename = next_arg(&args);
             fb->Upload(filename);
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 14ee785..99a4873 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -144,7 +144,8 @@
     return Flash(partition);
 }
 
-RetCode FastBootDriver::FlashPartition(const std::string& partition, int fd, uint32_t size) {
+RetCode FastBootDriver::FlashPartition(const std::string& partition, android::base::borrowed_fd fd,
+                                       uint32_t size) {
     RetCode ret;
     if ((ret = Download(partition, fd, size))) {
         return ret;
@@ -182,15 +183,16 @@
     return SUCCESS;
 }
 
-RetCode FastBootDriver::Download(const std::string& name, int fd, size_t size,
-                                 std::string* response, std::vector<std::string>* info) {
+RetCode FastBootDriver::Download(const std::string& name, android::base::borrowed_fd fd,
+                                 size_t size, std::string* response,
+                                 std::vector<std::string>* info) {
     prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), size / 1024));
     auto result = Download(fd, size, response, info);
     epilog_(result);
     return result;
 }
 
-RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
+RetCode FastBootDriver::Download(android::base::borrowed_fd fd, size_t size, std::string* response,
                                  std::vector<std::string>* info) {
     RetCode ret;
 
@@ -521,7 +523,7 @@
 }
 
 /******************************* PRIVATE **************************************/
-RetCode FastBootDriver::SendBuffer(int fd, size_t size) {
+RetCode FastBootDriver::SendBuffer(android::base::borrowed_fd fd, size_t size) {
     static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
     off64_t offset = 0;
     uint32_t remaining = size;
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index f1c094f..bccd668 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -77,9 +77,9 @@
     RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
     RetCode CreatePartition(const std::string& partition, const std::string& size);
     RetCode DeletePartition(const std::string& partition);
-    RetCode Download(const std::string& name, int fd, size_t size, std::string* response = nullptr,
-                     std::vector<std::string>* info = nullptr);
-    RetCode Download(int fd, size_t size, std::string* response = nullptr,
+    RetCode Download(const std::string& name, android::base::borrowed_fd fd, size_t size,
+                     std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode Download(android::base::borrowed_fd fd, size_t size, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
     RetCode Download(const std::string& name, const std::vector<char>& buf,
                      std::string* response = nullptr, std::vector<std::string>* info = nullptr);
@@ -113,7 +113,8 @@
 
     /* HIGHER LEVEL COMMANDS -- Composed of the commands above */
     RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);
-    RetCode FlashPartition(const std::string& partition, int fd, uint32_t sz);
+    RetCode FlashPartition(const std::string& partition, android::base::borrowed_fd fd,
+                           uint32_t sz);
     RetCode FlashPartition(const std::string& partition, sparse_file* s, uint32_t sz,
                            size_t current, size_t total);
 
@@ -149,7 +150,7 @@
     Transport* transport_;
 
   private:
-    RetCode SendBuffer(int fd, size_t size);
+    RetCode SendBuffer(android::base::borrowed_fd fd, size_t size);
     RetCode SendBuffer(const std::vector<char>& buf);
     RetCode SendBuffer(const void* buf, size_t size);
 
diff --git a/fastboot/testdata/Android.bp b/fastboot/testdata/Android.bp
index 5debf5e..a490fe2 100644
--- a/fastboot/testdata/Android.bp
+++ b/fastboot/testdata/Android.bp
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 python_binary_host {
     name: "fastboot_gen_rand",
     visibility: [":__subpackages__"],
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 50f4f33..7e30509 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -198,7 +198,7 @@
 
     ~MappedDevice();
 
-    int fd() const { return fd_; }
+    int fd() const { return fd_.get(); }
     const std::string& path() const { return path_; }
 
   protected:
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 44a423c..7199b38 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -250,6 +250,8 @@
     }
 
     ops_ = ops_buffer;
+    ops_->shrink_to_fit();
+
     return true;
 }
 
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index a44de84..5eb2003 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -48,6 +48,17 @@
     return SnapshotManager::New()->Dump(std::cout);
 }
 
+bool MapCmdHandler(int, char** argv) {
+    android::base::InitLogging(argv, &android::base::StderrLogger);
+    using namespace std::chrono_literals;
+    return SnapshotManager::New()->MapAllSnapshots(5000ms);
+}
+
+bool UnmapCmdHandler(int, char** argv) {
+    android::base::InitLogging(argv, &android::base::StderrLogger);
+    return SnapshotManager::New()->UnmapAllSnapshots();
+}
+
 bool MergeCmdHandler(int /*argc*/, char** argv) {
     android::base::InitLogging(argv, &android::base::StderrLogger);
     LOG(WARNING) << "Deprecated. Call update_engine_client --merge instead.";
@@ -58,6 +69,8 @@
         // clang-format off
         {"dump", DumpCmdHandler},
         {"merge", MergeCmdHandler},
+        {"map", MapCmdHandler},
+        {"unmap", UnmapCmdHandler},
         // clang-format on
 };
 
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index f1fcb70..5ef9e29 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -230,7 +230,7 @@
 
 
         // Store operation pointer.
-        chunk_map_[ChunkToSector(data_chunk_id)] = cow_op;
+        chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
         num_ops += 1;
         offset += sizeof(struct disk_exception);
         cowop_riter_->Next();
@@ -422,7 +422,7 @@
             de->new_chunk = data_chunk_id;
 
             // Store operation pointer.
-            chunk_map_[ChunkToSector(data_chunk_id)] = it->second;
+            chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), it->second));
             offset += sizeof(struct disk_exception);
             num_ops += 1;
             copy_ops++;
@@ -468,6 +468,12 @@
                         << "Areas : " << vec_.size();
     }
 
+    chunk_vec_.shrink_to_fit();
+    vec_.shrink_to_fit();
+
+    // Sort the vector based on sectors as we need this during un-aligned access
+    std::sort(chunk_vec_.begin(), chunk_vec_.end(), compare);
+
     SNAP_LOG(INFO) << "ReadMetadata completed. Final-chunk-id: " << data_chunk_id
                    << " Num Sector: " << ChunkToSector(data_chunk_id)
                    << " Replace-ops: " << replace_ops << " Zero-ops: " << zero_ops
diff --git a/fs_mgr/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd.h
index 6ce64d8..9335364 100644
--- a/fs_mgr/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd.h
@@ -108,7 +108,7 @@
     bool ProcessIORequest();
     int ReadData(sector_t sector, size_t size);
     int ReadUnalignedSector(sector_t sector, size_t size,
-                            std::map<sector_t, const CowOperation*>::iterator& it);
+                            std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);
 
     // Processing COW operations
     bool ProcessCowOp(const CowOperation* cow_op);
@@ -164,16 +164,21 @@
     bool InitializeWorkers();
     std::shared_ptr<Snapuserd> GetSharedPtr() { return shared_from_this(); }
 
-    std::map<sector_t, const CowOperation*>& GetChunkMap() { return chunk_map_; }
+    std::vector<std::pair<sector_t, const CowOperation*>>& GetChunkVec() { return chunk_vec_; }
     const std::vector<std::unique_ptr<uint8_t[]>>& GetMetadataVec() const { return vec_; }
 
+    static bool compare(std::pair<sector_t, const CowOperation*> p1,
+                        std::pair<sector_t, const CowOperation*> p2) {
+        return p1.first < p2.first;
+    }
+
   private:
     std::vector<std::unique_ptr<WorkerThread>> worker_threads_;
 
-    bool ReadMetadata();
     bool IsChunkIdMetadata(chunk_t chunk);
     chunk_t GetNextAllocatableChunkId(chunk_t chunk_id);
 
+    bool ReadMetadata();
     sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
     chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
     bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
@@ -197,15 +202,9 @@
     // mapping of old-chunk to new-chunk
     std::vector<std::unique_ptr<uint8_t[]>> vec_;
 
-    // Key - Sector
-    // Value - cow operation
-    //
-    // chunk_map stores the pseudo mapping of sector
-    // to COW operations. Each COW op is 4k; however,
-    // we can get a read request which are as small
-    // as 512 bytes. Hence, we need to binary search
-    // in the chunk_map to find the nearest COW op.
-    std::map<sector_t, const CowOperation*> chunk_map_;
+    // chunk_vec stores the pseudo mapping of sector
+    // to COW operations.
+    std::vector<std::pair<sector_t, const CowOperation*>> chunk_vec_;
 
     std::mutex lock_;
 
diff --git a/fs_mgr/libsnapshot/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd_worker.cpp
index 16f47fe..1002569 100644
--- a/fs_mgr/libsnapshot/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_worker.cpp
@@ -185,12 +185,13 @@
     return false;
 }
 
-int WorkerThread::ReadUnalignedSector(sector_t sector, size_t size,
-                                      std::map<sector_t, const CowOperation*>::iterator& it) {
+int WorkerThread::ReadUnalignedSector(
+        sector_t sector, size_t size,
+        std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
     size_t skip_sector_size = 0;
 
     SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
-                    << " Aligned sector: " << it->second;
+                    << " Aligned sector: " << it->first;
 
     if (!ProcessCowOp(it->second)) {
         SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size;
@@ -223,7 +224,8 @@
  *
  */
 int WorkerThread::ReadData(sector_t sector, size_t size) {
-    std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
+    std::vector<std::pair<sector_t, const CowOperation*>>::iterator it;
     /*
      * chunk_map stores COW operation at 4k granularity.
      * If the requested IO with the sector falls on the 4k
@@ -234,10 +236,16 @@
      * then we will have the find the nearest COW operation
      * and chop the 4K block to fetch the requested sector.
      */
-    std::map<sector_t, const CowOperation*>::iterator it = chunk_map.find(sector);
-    if (it == chunk_map.end()) {
-        it = chunk_map.lower_bound(sector);
-        if (it != chunk_map.begin()) {
+    it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
+                          Snapuserd::compare);
+
+    CHECK(it != chunk_vec.end());
+
+    // We didn't find the required sector; hence find the previous sector
+    // as lower_bound will gives us the value greater than
+    // the requested sector
+    if (it->first != sector) {
+        if (it != chunk_vec.begin()) {
             --it;
         }
 
@@ -380,7 +388,7 @@
 int WorkerThread::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
                                        int unmerged_exceptions) {
     int merged_ops_cur_iter = 0;
-    std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
 
     // Find the operations which are merged in this cycle.
     while ((unmerged_exceptions + merged_ops_cur_iter) < exceptions_per_area_) {
@@ -395,7 +403,13 @@
         if (cow_de->new_chunk != 0) {
             merged_ops_cur_iter += 1;
             offset += sizeof(struct disk_exception);
-            const CowOperation* cow_op = chunk_map[ChunkToSector(cow_de->new_chunk)];
+            auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
+                                       std::make_pair(ChunkToSector(cow_de->new_chunk), nullptr),
+                                       Snapuserd::compare);
+            CHECK(it != chunk_vec.end());
+            CHECK(it->first == ChunkToSector(cow_de->new_chunk));
+            const CowOperation* cow_op = it->second;
+
             CHECK(cow_op != nullptr);
 
             CHECK(cow_op->new_block == cow_de->old_chunk);
@@ -510,14 +524,18 @@
         return true;
     }
 
-    std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
     size_t remaining_size = header->len;
     size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
     CHECK(read_size == BLOCK_SZ) << "DmuserWriteRequest: read_size: " << read_size;
 
     CHECK(header->sector > 0);
     chunk_t chunk = SectorToChunk(header->sector);
-    CHECK(chunk_map.find(header->sector) == chunk_map.end());
+    auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
+                               std::make_pair(header->sector, nullptr), Snapuserd::compare);
+
+    bool not_found = (it == chunk_vec.end() || it->first != header->sector);
+    CHECK(not_found);
 
     void* buffer = bufsink_.GetPayloadBuffer(read_size);
     CHECK(buffer != nullptr);
@@ -550,7 +568,7 @@
     size_t remaining_size = header->len;
     loff_t offset = 0;
     sector_t sector = header->sector;
-    std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
     do {
         size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
 
@@ -568,8 +586,10 @@
             ConstructKernelCowHeader();
             SNAP_LOG(DEBUG) << "Kernel header constructed";
         } else {
-            if (!offset && (read_size == BLOCK_SZ) &&
-                chunk_map.find(header->sector) == chunk_map.end()) {
+            auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
+                                       std::make_pair(header->sector, nullptr), Snapuserd::compare);
+            bool not_found = (it == chunk_vec.end() || it->first != header->sector);
+            if (!offset && (read_size == BLOCK_SZ) && not_found) {
                 if (!ReadDiskExceptions(chunk, read_size)) {
                     SNAP_LOG(ERROR) << "ReadDiskExceptions failed for chunk id: " << chunk
                                     << "Sector: " << header->sector;
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 49e8085..95e814b 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -40,8 +40,6 @@
         "libbase",
         "libutils",
         "libcrypto",
-        "libkeystore_aidl",
-        "libkeystore_binder",
         "libhidlbase",
         "android.hardware.gatekeeper@1.0",
         "libgatekeeper_aidl",
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index f9c0cdd..8792c83 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -29,13 +29,11 @@
 #include <android-base/properties.h>
 #include <android/binder_ibinder.h>
 #include <android/binder_manager.h>
-#include <android/security/keystore/IKeystoreService.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
 #include <gatekeeper/password_handle.h>  // for password_handle_t
 #include <hardware/hw_auth_token.h>
-#include <keystore/keystore_return_types.h>
 #include <libgsi/libgsi.h>
 #include <log/log.h>
 #include <utils/String16.h>
@@ -303,7 +301,7 @@
             if (gkResponse->payload().size() != 0) {
                 // try to connect to IKeystoreAuthorization AIDL service first.
                 AIBinder* authzAIBinder =
-                        AServiceManager_checkService("android.security.authorization");
+                        AServiceManager_getService("android.security.authorization");
                 ::ndk::SpAIBinder authzBinder(authzAIBinder);
                 auto authzService = IKeystoreAuthorization::fromBinder(authzBinder);
                 if (authzService) {
@@ -328,21 +326,6 @@
                         LOG(ERROR) << "Failure in sending AuthToken to AuthorizationService.";
                         return GK_ERROR;
                     }
-                }
-                sp<IServiceManager> sm = defaultServiceManager();
-
-                sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
-                sp<security::keystore::IKeystoreService> service =
-                        interface_cast<security::keystore::IKeystoreService>(binder);
-
-                if (service) {
-                    int result = 0;
-                    auto binder_result = service->addAuthToken(gkResponse->payload(), &result);
-                    if (!binder_result.isOk() ||
-                        !keystore::KeyStoreServiceReturnCode(result).isOk()) {
-                        LOG(ERROR) << "Failure sending auth token to KeyStore: " << result;
-                        return GK_ERROR;
-                    }
                 } else {
                     LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with "
                                   "Keystore.";
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 382f430..27ace7d 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1172,7 +1172,8 @@
         } else if (StartsWith(key, "qemu."sv)) {
             InitPropertySet("ro.kernel." + key, value);
         } else if (key == "qemu") {
-            InitPropertySet("ro.kernel." + key, value);  // emulator specific, deprecated
+            // emulator specific, should be retired once emulator migrates to
+            // androidboot.
             InitPropertySet("ro.boot." + key, value);
         }
     });
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 294ff7b..a04ae9f 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -1145,6 +1145,11 @@
 on property:sys.sysctl.extra_free_kbytes=*
     write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}
 
+# Allow users to drop caches
+on property:perf.drop_caches=3
+    write /proc/sys/vm/drop_caches 3
+    setprop perf.drop_caches 0
+
 # "tcp_default_init_rwnd" Is too long!
 on property:net.tcp_def_init_rwnd=*
     write /proc/sys/net/ipv4/tcp_default_init_rwnd ${net.tcp_def_init_rwnd}