libunwindstack: Add Memory::ReadTag() function for reading memory tags.

This uses an experimental Linux kernel API for reading the tags across
processes using ptrace.

Bug: 135772972

Test: Unit tests pass.
Change-Id: Ib1a09d9219166011de80cf250b756bb8a4bcdb0a
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ab59a4b..57c4c2e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -57,6 +57,7 @@
         "MapInfo.cpp",
         "Maps.cpp",
         "Memory.cpp",
+        "MemoryMte.cpp",
         "LocalUnwinder.cpp",
         "Regs.cpp",
         "RegsArm.cpp",
@@ -101,6 +102,16 @@
         "liblog",
         "liblzma",
     ],
+
+    header_libs: [
+        "bionic_libc_platform_headers",
+    ],
+
+    product_variables: {
+        experimental_mte: {
+            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+        },
+    },
 }
 
 cc_library {
@@ -213,6 +224,7 @@
         "tests/MemoryRangesTest.cpp",
         "tests/MemoryRemoteTest.cpp",
         "tests/MemoryTest.cpp",
+        "tests/MemoryMteTest.cpp",
         "tests/RegsInfoTest.cpp",
         "tests/RegsIterateTest.cpp",
         "tests/RegsStepIfSignalHandlerTest.cpp",
@@ -268,6 +280,16 @@
         "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
     ],
+
+    header_libs: [
+        "bionic_libc_platform_headers",
+    ],
+
+    product_variables: {
+        experimental_mte: {
+            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+        },
+    },
 }
 
 cc_test {
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 8de3d98..fac9085 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -324,6 +324,16 @@
   return ProcessVmRead(getpid(), addr, dst, size);
 }
 
+#if !defined(ANDROID_EXPERIMENTAL_MTE)
+long MemoryRemote::ReadTag(uint64_t) {
+  return -1;
+}
+
+long MemoryLocal::ReadTag(uint64_t) {
+  return -1;
+}
+#endif
+
 MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
                          uint64_t offset)
     : memory_(memory), begin_(begin), length_(length), offset_(offset) {}
diff --git a/libunwindstack/MemoryCache.h b/libunwindstack/MemoryCache.h
index 769d907..d97640d 100644
--- a/libunwindstack/MemoryCache.h
+++ b/libunwindstack/MemoryCache.h
@@ -33,6 +33,7 @@
   virtual ~MemoryCache() = default;
 
   size_t Read(uint64_t addr, void* dst, size_t size) override;
+  long ReadTag(uint64_t addr) override { return impl_->ReadTag(addr); }
 
   void Clear() override { cache_.clear(); }
 
diff --git a/libunwindstack/MemoryLocal.h b/libunwindstack/MemoryLocal.h
index 7e027cf..741f107 100644
--- a/libunwindstack/MemoryLocal.h
+++ b/libunwindstack/MemoryLocal.h
@@ -31,6 +31,7 @@
   bool IsLocal() const override { return true; }
 
   size_t Read(uint64_t addr, void* dst, size_t size) override;
+  long ReadTag(uint64_t addr) override;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/MemoryMte.cpp b/libunwindstack/MemoryMte.cpp
new file mode 100644
index 0000000..d1d0ebc
--- /dev/null
+++ b/libunwindstack/MemoryMte.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+
+#include <sys/ptrace.h>
+
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
+
+#include "MemoryLocal.h"
+#include "MemoryRemote.h"
+
+namespace unwindstack {
+
+long MemoryRemote::ReadTag(uint64_t addr) {
+#if defined(__aarch64__)
+  return ptrace(PTRACE_PEEKTAG, pid_, (void*)addr, nullptr);
+#else
+  (void)addr;
+  return -1;
+#endif
+}
+
+long MemoryLocal::ReadTag(uint64_t addr) {
+#if defined(__aarch64__)
+  // Check that the memory is readable first. This is racy with the ldg but there's not much
+  // we can do about it.
+  char data;
+  if (!mte_supported() || !Read(addr, &data, 1)) {
+    return -1;
+  }
+
+  __asm__ __volatile__(".arch_extension mte; ldg %0, [%0]" : "+r"(addr) : : "memory");
+  return (addr >> 56) & 0xf;
+#else
+  (void)addr;
+  return -1;
+#endif
+}
+
+}  // namespace unwindstack
+
+#endif
diff --git a/libunwindstack/MemoryRemote.h b/libunwindstack/MemoryRemote.h
index db367d6..dd09c88 100644
--- a/libunwindstack/MemoryRemote.h
+++ b/libunwindstack/MemoryRemote.h
@@ -32,6 +32,7 @@
   virtual ~MemoryRemote() = default;
 
   size_t Read(uint64_t addr, void* dst, size_t size) override;
+  long ReadTag(uint64_t addr) override;
 
   pid_t pid() { return pid_; }
 
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index ecd908a..26252b6 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -44,6 +44,7 @@
   virtual bool IsLocal() const { return false; }
 
   virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
+  virtual long ReadTag(uint64_t) { return -1; }
 
   bool ReadFully(uint64_t addr, void* dst, size_t size);
 
diff --git a/libunwindstack/tests/MemoryMteTest.cpp b/libunwindstack/tests/MemoryMteTest.cpp
new file mode 100644
index 0000000..3ae322e
--- /dev/null
+++ b/libunwindstack/tests/MemoryMteTest.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <gtest/gtest.h>
+
+#include <bionic/mte.h>
+
+#include "MemoryLocal.h"
+#include "MemoryRemote.h"
+#include "TestUtils.h"
+
+namespace unwindstack {
+
+static uintptr_t CreateTagMapping() {
+  uintptr_t mapping =
+      reinterpret_cast<uintptr_t>(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE,
+                                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+  if (reinterpret_cast<void*>(mapping) == MAP_FAILED) {
+    return 0;
+  }
+#if defined(__aarch64__)
+  __asm__ __volatile__(".arch_extension mte; stg %0, [%0]"
+                       :
+                       : "r"(mapping + (1ULL << 56))
+                       : "memory");
+#endif
+  return mapping;
+}
+
+TEST(MemoryMteTest, remote_read_tag) {
+#if !defined(__aarch64__)
+  GTEST_SKIP() << "Requires aarch64";
+#else
+  if (!mte_supported()) {
+    GTEST_SKIP() << "Requires MTE";
+  }
+#endif
+
+  uintptr_t mapping = CreateTagMapping();
+  ASSERT_NE(0U, mapping);
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_TRUE(TestAttach(pid));
+
+  MemoryRemote remote(pid);
+
+  EXPECT_EQ(1, remote.ReadTag(mapping));
+  EXPECT_EQ(0, remote.ReadTag(mapping + 16));
+
+  ASSERT_TRUE(TestDetach(pid));
+}
+
+TEST(MemoryMteTest, local_read_tag) {
+#if !defined(__aarch64__)
+  GTEST_SKIP() << "Requires aarch64";
+#else
+  if (!mte_supported()) {
+    GTEST_SKIP() << "Requires MTE";
+  }
+#endif
+
+  uintptr_t mapping = CreateTagMapping();
+  ASSERT_NE(0U, mapping);
+
+  MemoryLocal local;
+
+  EXPECT_EQ(1, local.ReadTag(mapping));
+  EXPECT_EQ(0, local.ReadTag(mapping + 16));
+}
+
+}  // namespace unwindstack
+
+#endif
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index c90dedc..385078d 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -26,8 +26,10 @@
 
 #include <vector>
 
-#include <android-base/test_utils.h>
 #include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
 #include <gtest/gtest.h>
 
 #include "MemoryRemote.h"
@@ -37,24 +39,7 @@
 
 namespace unwindstack {
 
-class MemoryRemoteTest : public ::testing::Test {
- protected:
-  static bool Attach(pid_t pid) {
-    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
-      return false;
-    }
-
-    return TestQuiescePid(pid);
-  }
-
-  static bool Detach(pid_t pid) {
-    return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
-  }
-
-  static constexpr size_t NS_PER_SEC = 1000000000ULL;
-};
-
-TEST_F(MemoryRemoteTest, read) {
+TEST(MemoryRemoteTest, read) {
   std::vector<uint8_t> src(1024);
   memset(src.data(), 0x4c, 1024);
 
@@ -66,7 +51,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -76,10 +61,10 @@
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_large) {
+TEST(MemoryRemoteTest, read_large) {
   static constexpr size_t kTotalPages = 245;
   std::vector<uint8_t> src(kTotalPages * getpagesize());
   for (size_t i = 0; i < kTotalPages; i++) {
@@ -95,7 +80,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -105,10 +90,10 @@
     ASSERT_EQ(i / getpagesize(), dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_partial) {
+TEST(MemoryRemoteTest, read_partial) {
   char* mapping = static_cast<char*>(
       mmap(nullptr, 4 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
   ASSERT_NE(MAP_FAILED, mapping);
@@ -128,7 +113,7 @@
   // Unmap from our process.
   ASSERT_EQ(0, munmap(mapping, 3 * getpagesize()));
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -149,10 +134,10 @@
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_fail) {
+TEST(MemoryRemoteTest, read_fail) {
   int pagesize = getpagesize();
   void* src = mmap(nullptr, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,-1, 0);
   memset(src, 0x4c, pagesize * 2);
@@ -169,7 +154,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -188,10 +173,10 @@
 
   ASSERT_EQ(0, munmap(src, pagesize));
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_overflow) {
+TEST(MemoryRemoteTest, read_overflow) {
   pid_t pid;
   if ((pid = fork()) == 0) {
     while (true)
@@ -201,7 +186,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -209,10 +194,10 @@
   std::vector<uint8_t> dst(200);
   ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_illegal) {
+TEST(MemoryRemoteTest, read_illegal) {
   pid_t pid;
   if ((pid = fork()) == 0) {
     while (true);
@@ -221,7 +206,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -229,10 +214,10 @@
   ASSERT_FALSE(remote.ReadFully(0, dst.data(), 1));
   ASSERT_FALSE(remote.ReadFully(0, dst.data(), 100));
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_mprotect_hole) {
+TEST(MemoryRemoteTest, read_mprotect_hole) {
   size_t page_size = getpagesize();
   void* mapping =
       mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -250,7 +235,7 @@
 
   ASSERT_EQ(0, munmap(mapping, 3 * page_size));
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
   std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -263,9 +248,11 @@
   for (size_t i = read_size; i < dst.size(); ++i) {
     ASSERT_EQ(0xCC, dst[i]);
   }
+
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_munmap_hole) {
+TEST(MemoryRemoteTest, read_munmap_hole) {
   size_t page_size = getpagesize();
   void* mapping =
       mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -285,7 +272,7 @@
   ASSERT_EQ(0, munmap(mapping, page_size));
   ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + 2 * page_size, page_size));
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
   std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -297,11 +284,13 @@
   for (size_t i = read_size; i < dst.size(); ++i) {
     ASSERT_EQ(0xCC, dst[i]);
   }
+
+  ASSERT_TRUE(TestDetach(pid));
 }
 
 // Verify that the memory remote object chooses a memory read function
 // properly. Either process_vm_readv or ptrace.
-TEST_F(MemoryRemoteTest, read_choose_correctly) {
+TEST(MemoryRemoteTest, read_choose_correctly) {
   size_t page_size = getpagesize();
   void* mapping =
       mmap(nullptr, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -320,7 +309,7 @@
 
   ASSERT_EQ(0, munmap(mapping, 2 * page_size));
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   // We know that process_vm_readv of a mprotect'd PROT_NONE region will fail.
   // Read from the PROT_NONE area first to force the choice of ptrace.
@@ -348,6 +337,8 @@
   bytes = remote_readv.Read(reinterpret_cast<uint64_t>(mapping) + page_size, &value, sizeof(value));
   ASSERT_EQ(sizeof(value), bytes);
   ASSERT_EQ(0xfcfcfcfcU, value);
+
+  ASSERT_TRUE(TestDetach(pid));
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
index a4d7b9b..0685006 100644
--- a/libunwindstack/tests/TestUtils.h
+++ b/libunwindstack/tests/TestUtils.h
@@ -21,6 +21,7 @@
 #include <sys/ptrace.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 namespace unwindstack {
 
@@ -50,6 +51,18 @@
   return ready;
 }
 
+inline bool TestAttach(pid_t pid) {
+  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+    return false;
+  }
+
+  return TestQuiescePid(pid);
+}
+
+inline bool TestDetach(pid_t pid) {
+  return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
+}
+
 void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
 
 }  // namespace unwindstack