linux,arm: support memory tagging

64-bit ARM's Top-Byte-Ignore enables features such as memory tagging.
https://www.kernel.org/doc/html/latest/arm64/tagged-address-abi.html

Android 11 will start using memory tagging on some devices.
https://source.android.com/devices/tech/debug/tagged-pointers

Crashpad needs to remove the tags from pointers before comparing to
addresses or using with system calls.

Bug: crashpad:364
Change-Id: I67c6b9a4a86d090e1d139de727eb06d9e222cc25
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3078500
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
diff --git a/snapshot/linux/process_reader_linux.cc b/snapshot/linux/process_reader_linux.cc
index ee246e8..a9098ac 100644
--- a/snapshot/linux/process_reader_linux.cc
+++ b/snapshot/linux/process_reader_linux.cc
@@ -125,7 +125,8 @@
     LOG(WARNING) << "no stack mapping";
     return;
   }
-  LinuxVMAddress stack_region_start = stack_pointer;
+  LinuxVMAddress stack_region_start =
+      reader->Memory()->PointerToAddress(stack_pointer);
 
   // We've hit what looks like a guard page; skip to the end and check for a
   // mapped stack region.
@@ -177,11 +178,11 @@
   // at the high-address end of the stack so we can try using that to shrink
   // the stack region.
   stack_region_size = stack_end - stack_region_address;
-  if (tid != reader->ProcessID() &&
-      thread_info.thread_specific_data_address > stack_region_address &&
-      thread_info.thread_specific_data_address < stack_end) {
-    stack_region_size =
-        thread_info.thread_specific_data_address - stack_region_address;
+  VMAddress tls_address = reader->Memory()->PointerToAddress(
+      thread_info.thread_specific_data_address);
+  if (tid != reader->ProcessID() && tls_address > stack_region_address &&
+      tls_address < stack_end) {
+    stack_region_size = tls_address - stack_region_address;
   }
 }
 
diff --git a/snapshot/linux/process_reader_linux.h b/snapshot/linux/process_reader_linux.h
index 0eb1d40..f44e15f 100644
--- a/snapshot/linux/process_reader_linux.h
+++ b/snapshot/linux/process_reader_linux.h
@@ -123,7 +123,7 @@
   pid_t ParentProcessID() const { return process_info_.ParentProcessID(); }
 
   //! \brief Return a memory reader for the target process.
-  const ProcessMemory* Memory() const { return connection_->Memory(); }
+  const ProcessMemoryLinux* Memory() const { return connection_->Memory(); }
 
   //! \brief Return a memory map of the target process.
   MemoryMap* GetMemoryMap() { return &memory_map_; }
diff --git a/snapshot/linux/process_reader_linux_test.cc b/snapshot/linux/process_reader_linux_test.cc
index 250f12d..f3791f8 100644
--- a/snapshot/linux/process_reader_linux_test.cc
+++ b/snapshot/linux/process_reader_linux_test.cc
@@ -291,9 +291,12 @@
 #if !defined(ADDRESS_SANITIZER)
     // AddressSanitizer causes stack variables to be stored separately from the
     // call stack.
-    EXPECT_LE(thread.stack_region_address, iterator->second.stack_address);
-    EXPECT_GE(thread.stack_region_address + thread.stack_region_size,
-              iterator->second.stack_address);
+    EXPECT_LE(
+        thread.stack_region_address,
+        connection->Memory()->PointerToAddress(iterator->second.stack_address));
+    EXPECT_GE(
+        thread.stack_region_address + thread.stack_region_size,
+        connection->Memory()->PointerToAddress(iterator->second.stack_address));
 #endif  // !defined(ADDRESS_SANITIZER)
 
     if (iterator->second.max_stack_size) {
diff --git a/test/linux/fake_ptrace_connection.cc b/test/linux/fake_ptrace_connection.cc
index 774a340..33a8f08 100644
--- a/test/linux/fake_ptrace_connection.cc
+++ b/test/linux/fake_ptrace_connection.cc
@@ -80,7 +80,7 @@
   return LoggingReadEntireFile(path, contents);
 }
 
-ProcessMemory* FakePtraceConnection::Memory() {
+ProcessMemoryLinux* FakePtraceConnection::Memory() {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   if (!memory_) {
     memory_ = std::make_unique<ProcessMemoryLinux>(this);
diff --git a/test/linux/fake_ptrace_connection.h b/test/linux/fake_ptrace_connection.h
index bf09d95..f6ba7de 100644
--- a/test/linux/fake_ptrace_connection.h
+++ b/test/linux/fake_ptrace_connection.h
@@ -63,7 +63,7 @@
 
   //! \brief Attempts to create a ProcessMemory when called, calling
   //!     ADD_FAILURE() and returning `nullptr` on failure.
-  ProcessMemory* Memory() override;
+  ProcessMemoryLinux* Memory() override;
 
   //! \todo Not yet implemented.
   bool Threads(std::vector<pid_t>* threads) override;
diff --git a/util/linux/direct_ptrace_connection.cc b/util/linux/direct_ptrace_connection.cc
index 5da0147..3cc41c3 100644
--- a/util/linux/direct_ptrace_connection.cc
+++ b/util/linux/direct_ptrace_connection.cc
@@ -73,7 +73,7 @@
   return LoggingReadEntireFile(path, contents);
 }
 
-ProcessMemory* DirectPtraceConnection::Memory() {
+ProcessMemoryLinux* DirectPtraceConnection::Memory() {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   if (!memory_) {
     memory_ = std::make_unique<ProcessMemoryLinux>(this);
diff --git a/util/linux/direct_ptrace_connection.h b/util/linux/direct_ptrace_connection.h
index a120889..391495f 100644
--- a/util/linux/direct_ptrace_connection.h
+++ b/util/linux/direct_ptrace_connection.h
@@ -58,13 +58,13 @@
   bool GetThreadInfo(pid_t tid, ThreadInfo* info) override;
   bool ReadFileContents(const base::FilePath& path,
                         std::string* contents) override;
-  ProcessMemory* Memory() override;
+  ProcessMemoryLinux* Memory() override;
   bool Threads(std::vector<pid_t>* threads) override;
   ssize_t ReadUpTo(VMAddress, size_t size, void* buffer) override;
 
  private:
   std::vector<std::unique_ptr<ScopedPtraceAttach>> attachments_;
-  std::unique_ptr<ProcessMemory> memory_;
+  std::unique_ptr<ProcessMemoryLinux> memory_;
   pid_t pid_;
   Ptracer ptracer_;
   InitializationStateDcheck initialized_;
diff --git a/util/linux/memory_map.cc b/util/linux/memory_map.cc
index dabc114..e15a1c5 100644
--- a/util/linux/memory_map.cc
+++ b/util/linux/memory_map.cc
@@ -240,7 +240,7 @@
       executable(false),
       shareable(false) {}
 
-MemoryMap::MemoryMap() : mappings_(), initialized_() {}
+MemoryMap::MemoryMap() : mappings_(), connection_(nullptr), initialized_() {}
 
 MemoryMap::~MemoryMap() {}
 
@@ -256,6 +256,7 @@
 
 bool MemoryMap::Initialize(PtraceConnection* connection) {
   INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+  connection_ = connection;
 
   // If the maps file is not read atomically, entries can be read multiple times
   // or missed entirely. The kernel reads entries from this file into a page
@@ -268,8 +269,8 @@
   do {
     std::string contents;
     char path[32];
-    snprintf(path, sizeof(path), "/proc/%d/maps", connection->GetProcessID());
-    if (!connection->ReadFileContents(base::FilePath(path), &contents)) {
+    snprintf(path, sizeof(path), "/proc/%d/maps", connection_->GetProcessID());
+    if (!connection_->ReadFileContents(base::FilePath(path), &contents)) {
       return false;
     }
 
@@ -298,6 +299,7 @@
 
 const MemoryMap::Mapping* MemoryMap::FindMapping(LinuxVMAddress address) const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  address = connection_->Memory()->PointerToAddress(address);
 
   for (const auto& mapping : mappings_) {
     if (mapping.range.Base() <= address && mapping.range.End() > address) {
diff --git a/util/linux/memory_map.h b/util/linux/memory_map.h
index d43b7af..f6f7cc8 100644
--- a/util/linux/memory_map.h
+++ b/util/linux/memory_map.h
@@ -129,6 +129,7 @@
 
  private:
   std::vector<Mapping> mappings_;
+  PtraceConnection* connection_;
   InitializationStateDcheck initialized_;
 };
 
diff --git a/util/linux/ptrace_client.cc b/util/linux/ptrace_client.cc
index cb1a1a5..9a34722 100644
--- a/util/linux/ptrace_client.cc
+++ b/util/linux/ptrace_client.cc
@@ -248,7 +248,7 @@
   return true;
 }
 
-ProcessMemory* PtraceClient::Memory() {
+ProcessMemoryLinux* PtraceClient::Memory() {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   if (!memory_) {
     memory_ = std::make_unique<ProcessMemoryLinux>(this);
diff --git a/util/linux/ptrace_client.h b/util/linux/ptrace_client.h
index 299aafc..cd40f6b 100644
--- a/util/linux/ptrace_client.h
+++ b/util/linux/ptrace_client.h
@@ -60,14 +60,14 @@
   bool GetThreadInfo(pid_t tid, ThreadInfo* info) override;
   bool ReadFileContents(const base::FilePath& path,
                         std::string* contents) override;
-  ProcessMemory* Memory() override;
+  ProcessMemoryLinux* Memory() override;
   bool Threads(std::vector<pid_t>* threads) override;
   ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) override;
 
  private:
   bool SendFilePath(const char* path, size_t length);
 
-  std::unique_ptr<ProcessMemory> memory_;
+  std::unique_ptr<ProcessMemoryLinux> memory_;
   int sock_;
   pid_t pid_;
   bool is_64_bit_;
diff --git a/util/linux/ptrace_connection.h b/util/linux/ptrace_connection.h
index 9b31d52..15594f8 100644
--- a/util/linux/ptrace_connection.h
+++ b/util/linux/ptrace_connection.h
@@ -22,7 +22,7 @@
 
 #include "base/files/file_path.h"
 #include "util/linux/thread_info.h"
-#include "util/process/process_memory.h"
+#include "util/process/process_memory_linux.h"
 
 namespace crashpad {
 
@@ -64,7 +64,7 @@
   //!
   //! The caller does not take ownership of the reader. The reader is valid for
   //! the lifetime of the PtraceConnection that created it.
-  virtual ProcessMemory* Memory() = 0;
+  virtual ProcessMemoryLinux* Memory() = 0;
 
   //! \brief Determines the thread IDs of the threads in the connected process.
   //!
diff --git a/util/process/process_memory_linux.cc b/util/process/process_memory_linux.cc
index 56ac0ec..b67b9c0 100644
--- a/util/process/process_memory_linux.cc
+++ b/util/process/process_memory_linux.cc
@@ -24,11 +24,20 @@
 
 #include "base/logging.h"
 #include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "util/file/filesystem.h"
+#include "util/linux/ptrace_connection.h"
 
 namespace crashpad {
 
 ProcessMemoryLinux::ProcessMemoryLinux(PtraceConnection* connection)
-    : ProcessMemory(), mem_fd_() {
+    : ProcessMemory(), mem_fd_(), ignore_top_byte_(false) {
+#if defined(ARCH_CPU_ARM_FAMILY)
+  if (connection->Is64Bit()) {
+    ignore_top_byte_ = true;
+  }
+#endif  // ARCH_CPU_ARM_FAMILY
+
   char path[32];
   snprintf(path, sizeof(path), "/proc/%d/mem", connection->GetProcessID());
   mem_fd_.reset(HANDLE_EINTR(open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC)));
@@ -53,11 +62,15 @@
 
 ProcessMemoryLinux::~ProcessMemoryLinux() {}
 
+VMAddress ProcessMemoryLinux::PointerToAddress(VMAddress address) const {
+  return ignore_top_byte_ ? address & 0x00ffffffffffffff : address;
+}
+
 ssize_t ProcessMemoryLinux::ReadUpTo(VMAddress address,
                                      size_t size,
                                      void* buffer) const {
   DCHECK_LE(size, size_t{std::numeric_limits<ssize_t>::max()});
-  return read_up_to_(address, size, buffer);
+  return read_up_to_(PointerToAddress(address), size, buffer);
 }
 
 }  // namespace crashpad
diff --git a/util/process/process_memory_linux.h b/util/process/process_memory_linux.h
index 496cf2d..f62f2b4 100644
--- a/util/process/process_memory_linux.h
+++ b/util/process/process_memory_linux.h
@@ -21,12 +21,13 @@
 #include <string>
 
 #include "base/files/scoped_file.h"
-#include "util/linux/ptrace_connection.h"
 #include "util/misc/address_types.h"
 #include "util/process/process_memory.h"
 
 namespace crashpad {
 
+class PtraceConnection;
+
 //! \brief Accesses the memory of another Linux process.
 class ProcessMemoryLinux final : public ProcessMemory {
  public:
@@ -37,11 +38,16 @@
 
   ~ProcessMemoryLinux();
 
+  //! \brief Returns the input pointer with any non-addressing bits, such as
+  //!     tags removed.
+  VMAddress PointerToAddress(VMAddress address) const;
+
  private:
   ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;
 
   std::function<ssize_t(VMAddress, size_t, void*)> read_up_to_;
   base::ScopedFD mem_fd_;
+  bool ignore_top_byte_;
 };
 
 }  // namespace crashpad