[Lldb-commits] [lldb] d510b5f - [lldb][AArch64] Annotate synchronous tag faults

David Spickett via lldb-commits lldb-commits at lists.llvm.org
Thu Jul 29 02:26:44 PDT 2021


Author: David Spickett
Date: 2021-07-29T10:26:37+01:00
New Revision: d510b5f199d6e7a3062b5a6ea43181c4cc00a605

URL: https://github.com/llvm/llvm-project/commit/d510b5f199d6e7a3062b5a6ea43181c4cc00a605
DIFF: https://github.com/llvm/llvm-project/commit/d510b5f199d6e7a3062b5a6ea43181c4cc00a605.diff

LOG: [lldb][AArch64] Annotate synchronous tag faults

In the latest Linux kernels synchronous tag faults
include the tag bits in their address.
This change adds logical and allocation tags to the
description of synchronous tag faults.
(asynchronous faults have no address)

Process 1626 stopped
* thread #1, name = 'a.out', stop reason = signal SIGSEGV: sync tag check fault (fault address: 0x900fffff7ff9010 logical tag: 0x9 allocation tag: 0x0)

This extends the existing description and will
show as much as it can on the rare occasion something
fails.

This change supports AArch64 MTE only but other
architectures could be added by extending the
switch at the start of AnnotateSyncTagCheckFault.
The rest of the function is generic code.

Tests have been added for synchronous and asynchronous
MTE faults.

Reviewed By: omjavaid

Differential Revision: https://reviews.llvm.org/D105178

Added: 
    lldb/test/API/linux/aarch64/mte_tag_faults/Makefile
    lldb/test/API/linux/aarch64/mte_tag_faults/TestAArch64LinuxMTEMemoryTagFaults.py
    lldb/test/API/linux/aarch64/mte_tag_faults/main.c

Modified: 
    lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
    lldb/source/Plugins/Process/Linux/NativeThreadLinux.h

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
index d8ba5415a983e..a7e4e9b13ff0a 100644
--- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
@@ -26,6 +26,7 @@
 #include "llvm/ADT/SmallString.h"
 
 #include "Plugins/Process/POSIX/CrashReason.h"
+#include "Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h"
 
 #include <sys/syscall.h>
 // Try to define a macro to encapsulate the tgkill syscall
@@ -299,11 +300,69 @@ void NativeThreadLinux::SetStoppedBySignal(uint32_t signo,
               ? CrashReason::eInvalidAddress
               : GetCrashReason(*info);
       m_stop_description = GetCrashReasonString(reason, *info);
+
+      if (reason == CrashReason::eSyncTagCheckFault) {
+        AnnotateSyncTagCheckFault(info);
+      }
+
       break;
     }
   }
 }
 
+void NativeThreadLinux::AnnotateSyncTagCheckFault(const siginfo_t *info) {
+  int32_t allocation_tag_type = 0;
+  switch (GetProcess().GetArchitecture().GetMachine()) {
+  // aarch64_32 deliberately not here because there's no 32 bit MTE
+  case llvm::Triple::aarch64:
+  case llvm::Triple::aarch64_be:
+    allocation_tag_type = MemoryTagManagerAArch64MTE::eMTE_allocation;
+    break;
+  default:
+    return;
+  }
+
+  auto details =
+      GetRegisterContext().GetMemoryTaggingDetails(allocation_tag_type);
+  if (!details) {
+    llvm::consumeError(details.takeError());
+    return;
+  }
+
+  // We assume that the stop description is currently:
+  // signal SIGSEGV: sync tag check fault (fault address: <addr>)
+  // Remove the closing )
+  m_stop_description.pop_back();
+
+  std::stringstream ss;
+  lldb::addr_t fault_addr = reinterpret_cast<uintptr_t>(info->si_addr);
+  std::unique_ptr<MemoryTagManager> manager(std::move(details->manager));
+
+  ss << " logical tag: 0x" << std::hex << manager->GetLogicalTag(fault_addr);
+
+  std::vector<uint8_t> allocation_tag_data;
+  // The fault address may not be granule aligned. ReadMemoryTags will granule
+  // align any range you give it, potentially making it larger.
+  // To prevent this set len to 1. This always results in a range that is at
+  // most 1 granule in size and includes fault_addr.
+  Status status = GetProcess().ReadMemoryTags(allocation_tag_type, fault_addr,
+                                              1, allocation_tag_data);
+
+  if (status.Success()) {
+    llvm::Expected<std::vector<lldb::addr_t>> allocation_tag =
+        manager->UnpackTagsData(allocation_tag_data, 1);
+    if (allocation_tag) {
+      ss << " allocation tag: 0x" << std::hex << allocation_tag->front() << ")";
+    } else {
+      llvm::consumeError(allocation_tag.takeError());
+      ss << ")";
+    }
+  } else
+    ss << ")";
+
+  m_stop_description += ss.str();
+}
+
 bool NativeThreadLinux::IsStopped(int *signo) {
   if (!StateIsStoppedState(m_state, false))
     return false;

diff  --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
index f03de755c7bf3..c18665b0107ef 100644
--- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
+++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
@@ -102,6 +102,11 @@ class NativeThreadLinux : public NativeThreadProtocol {
 
   void SetStopped();
 
+  /// Extend m_stop_description with logical and allocation tag values.
+  /// If there is an error along the way just add the information we were able
+  /// to get.
+  void AnnotateSyncTagCheckFault(const siginfo_t *info);
+
   // Member Variables
   lldb::StateType m_state;
   ThreadStopInfo m_stop_info;

diff  --git a/lldb/test/API/linux/aarch64/mte_tag_faults/Makefile b/lldb/test/API/linux/aarch64/mte_tag_faults/Makefile
new file mode 100644
index 0000000000000..01745f1889eac
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/mte_tag_faults/Makefile
@@ -0,0 +1,4 @@
+C_SOURCES := main.c
+CFLAGS_EXTRAS := -march=armv8.5-a+memtag
+
+include Makefile.rules

diff  --git a/lldb/test/API/linux/aarch64/mte_tag_faults/TestAArch64LinuxMTEMemoryTagFaults.py b/lldb/test/API/linux/aarch64/mte_tag_faults/TestAArch64LinuxMTEMemoryTagFaults.py
new file mode 100644
index 0000000000000..62234e674a9aa
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/mte_tag_faults/TestAArch64LinuxMTEMemoryTagFaults.py
@@ -0,0 +1,62 @@
+"""
+Test reporting of MTE tag access faults.
+"""
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class AArch64LinuxMTEMemoryTagFaultsTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def setup_mte_test(self, fault_type):
+        if not self.isAArch64MTE():
+            self.skipTest('Target must support MTE.')
+
+        self.build()
+        self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line(self, "main.c",
+            line_number('main.c', '// Breakpoint here'),
+            num_expected_locations=1)
+
+        self.runCmd("run {}".format(fault_type), RUN_SUCCEEDED)
+
+        if self.process().GetState() == lldb.eStateExited:
+            self.fail("Test program failed to run.")
+
+        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+            substrs=['stopped',
+                     'stop reason = breakpoint'])
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    @skipUnlessAArch64MTELinuxCompiler
+    def test_mte_tag_fault_sync(self):
+        self.setup_mte_test("sync")
+        # The logical tag should be included in the fault address
+        # and we know what the bottom byte should be.
+        # It will be 0x10 (to be in the 2nd granule), +1 to be 0x11.
+        # Which tests that lldb-server handles fault addresses that
+        # are not granule aligned.
+        self.expect("continue",
+                patterns=[
+                "\* thread #1, name = 'a.out', stop reason = signal SIGSEGV: "
+                "sync tag check fault \(fault address: 0x9[0-9A-Fa-f]+11\ "
+                "logical tag: 0x9 allocation tag: 0xa\)"])
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    @skipUnlessAArch64MTELinuxCompiler
+    def test_mte_tag_fault_async(self):
+        self.setup_mte_test("async")
+        self.expect("continue",
+                substrs=[
+                    "* thread #1, name = 'a.out', stop reason = "
+                    "signal SIGSEGV: async tag check fault"])

diff  --git a/lldb/test/API/linux/aarch64/mte_tag_faults/main.c b/lldb/test/API/linux/aarch64/mte_tag_faults/main.c
new file mode 100644
index 0000000000000..81fb3ce435a4f
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/mte_tag_faults/main.c
@@ -0,0 +1,59 @@
+#include <arm_acle.h>
+#include <asm/hwcap.h>
+#include <asm/mman.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/auxv.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+// Set bits 59-56 to tag, removing any existing tag
+static char *set_tag(char *ptr, size_t tag) {
+  return (char *)(((size_t)ptr & ~((size_t)0xf << 56)) | (tag << 56));
+}
+
+int main(int argc, char const *argv[]) {
+  // We assume that the test runner has checked we're on an MTE system
+
+  // Only expect to get the fault type
+  if (argc != 2)
+    return 1;
+
+  unsigned long prctl_arg2 = 0;
+  if (!strcmp(argv[1], "sync"))
+    prctl_arg2 = PR_MTE_TCF_SYNC;
+  else if (!strcmp(argv[1], "async"))
+    prctl_arg2 = PR_MTE_TCF_ASYNC;
+  else
+    return 1;
+
+  // Set fault type
+  if (prctl(PR_SET_TAGGED_ADDR_CTRL, prctl_arg2, 0, 0, 0))
+    return 1;
+
+  // Allocate some memory with tagging enabled that we
+  // can read/write if we use correct tags.
+  char *buf = mmap(0, sysconf(_SC_PAGESIZE), PROT_MTE | PROT_READ | PROT_WRITE,
+                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (buf == MAP_FAILED)
+    return 1;
+
+  // Our pointer will have tag 9
+  char *tagged_buf = set_tag(buf, 9);
+  // Set allocation tags for the first 2 granules
+  __arm_mte_set_tag(set_tag(tagged_buf, 9));
+  __arm_mte_set_tag(set_tag(tagged_buf + 16, 10));
+
+  // Confirm that we can write when tags match
+  *tagged_buf = ' ';
+
+  // Breakpoint here
+  // Faults because tag 9 in the ptr != allocation tag of 10.
+  // + 16 puts us in the second granule and +1 makes the fault address
+  // misaligned relative to the granule size. This misalignment must
+  // be accounted for by lldb-server.
+  *(tagged_buf + 16 + 1) = '?';
+
+  return 0;
+}


        


More information about the lldb-commits mailing list