[Lldb-commits] [lldb] 5e6aabd - Support AArch64/Linux watchpoint on tagged addresses

Muhammad Omair Javaid via lldb-commits lldb-commits at lists.llvm.org
Sun Jul 11 19:39:43 PDT 2021


Author: Muhammad Omair Javaid
Date: 2021-07-12T07:39:26+05:00
New Revision: 5e6aabd48e351cf2632c25cb8bdfd0598a5019a6

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

LOG: Support AArch64/Linux watchpoint on tagged addresses

AArch64 architecture support virtual addresses with some of the top bits ignored.
These ignored bits can host memory tags or bit masks that can serve to check for
authentication of address integrity. We need to clear away the top ignored bits
from watchpoint address to reliably hit and set watchpoints on addresses
containing tags or masks in their top bits.

This patch adds support to watch tagged addresses on AArch64/Linux.

Reviewed By: DavidSpickett

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

Added: 
    lldb/test/API/commands/watchpoints/watch_tagged_addr/Makefile
    lldb/test/API/commands/watchpoints/watch_tagged_addr/TestWatchTaggedAddress.py
    lldb/test/API/commands/watchpoints/watch_tagged_addr/main.c

Modified: 
    lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
    lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
    lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
    lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h
    lldb/source/Target/Target.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
index a0672a635937..34a520edb693 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
@@ -892,4 +892,19 @@ NativeRegisterContextLinux_arm64::GetMemoryTaggingDetails(int32_t type) {
                                  "Unknown AArch64 memory tag type %d", type);
 }
 
+lldb::addr_t NativeRegisterContextLinux_arm64::FixWatchpointHitAddress(
+    lldb::addr_t hit_addr) {
+  // Linux configures user-space virtual addresses with top byte ignored.
+  // We set default value of mask such that top byte is masked out.
+  lldb::addr_t mask = ~((1ULL << 56) - 1);
+
+  // Try to read pointer authentication data_mask register and calculate a
+  // consolidated data address mask after ignoring the top byte.
+  if (ReadPAuthMask().Success())
+    mask |= m_pac_mask.data_mask;
+
+  return hit_addr & ~mask;
+  ;
+}
+
 #endif // defined (__arm64__) || defined (__aarch64__)

diff  --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
index 6b56660fb80c..4dfc78b5b282 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
@@ -76,6 +76,8 @@ class NativeRegisterContextLinux_arm64
 
   size_t GetFPRSize() override { return sizeof(m_fpr); }
 
+  lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) override;
+
 private:
   bool m_gpr_is_valid;
   bool m_fpu_is_valid;

diff  --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
index 5c05baf71764..feee857cfe5f 100644
--- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
@@ -421,6 +421,9 @@ Status NativeRegisterContextDBReg_arm64::GetWatchpointHitIndex(
   if (error)
     return Status(std::move(error));
 
+  // Mask off ignored bits from watchpoint trap address.
+  trap_addr = FixWatchpointHitAddress(trap_addr);
+
   uint32_t watch_size;
   lldb::addr_t watch_addr;
 

diff  --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h
index 12ef5571f64c..3da0b0407ce6 100644
--- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h
@@ -72,6 +72,9 @@ class NativeRegisterContextDBReg_arm64
 
   virtual llvm::Error ReadHardwareDebugInfo() = 0;
   virtual llvm::Error WriteHardwareDebugRegs(DREGType hwbType) = 0;
+  virtual lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) {
+    return hit_addr;
+  }
 };
 
 } // namespace lldb_private

diff  --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 839eb5887893..6cb7a9942a5c 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -41,6 +41,7 @@
 #include "lldb/Symbol/Function.h"
 #include "lldb/Symbol/ObjectFile.h"
 #include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/ABI.h"
 #include "lldb/Target/Language.h"
 #include "lldb/Target/LanguageRuntime.h"
 #include "lldb/Target/Process.h"
@@ -823,6 +824,11 @@ WatchpointSP Target::CreateWatchpoint(lldb::addr_t addr, size_t size,
   // Grab the list mutex while doing operations.
   const bool notify = false; // Don't notify about all the state changes we do
                              // on creating the watchpoint.
+
+  // Mask off ignored bits from watchpoint address.
+  if (ABISP abi = m_process_sp->GetABI())
+    addr = abi->FixDataAddress(addr);
+
   std::unique_lock<std::recursive_mutex> lock;
   this->GetWatchpointList().GetListMutex(lock);
   WatchpointSP matched_sp = m_watchpoint_list.FindByAddress(addr);

diff  --git a/lldb/test/API/commands/watchpoints/watch_tagged_addr/Makefile b/lldb/test/API/commands/watchpoints/watch_tagged_addr/Makefile
new file mode 100644
index 000000000000..90555c6c9f60
--- /dev/null
+++ b/lldb/test/API/commands/watchpoints/watch_tagged_addr/Makefile
@@ -0,0 +1,5 @@
+C_SOURCES := main.c
+
+CFLAGS_EXTRAS := -march=armv8.3-a
+
+include Makefile.rules

diff  --git a/lldb/test/API/commands/watchpoints/watch_tagged_addr/TestWatchTaggedAddress.py b/lldb/test/API/commands/watchpoints/watch_tagged_addr/TestWatchTaggedAddress.py
new file mode 100644
index 000000000000..c7aab63c471f
--- /dev/null
+++ b/lldb/test/API/commands/watchpoints/watch_tagged_addr/TestWatchTaggedAddress.py
@@ -0,0 +1,135 @@
+"""
+Test LLDB can set and hit watchpoints on tagged addresses
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestWatchTaggedAddresses(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+
+        # Set source filename.
+        self.source = 'main.c'
+
+        # Invoke the default build rule.
+        self.build()
+
+        # Get the path of the executable
+        exe = self.getBuildArtifact("a.out")
+
+        # Create a target by the debugger.
+        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+
+    @skipIf(archs=no_match(["aarch64"]))
+    @skipIf(oslist=no_match(['linux']))
+    def test_watch_hit_tagged_ptr_access(self):
+        """
+        Test that LLDB hits watchpoint installed on an untagged address with
+        memory access by a tagged pointer.
+        """
+        if not self.isAArch64PAuth():
+            self.skipTest('Target must support pointer authentication.')
+
+        # Add a breakpoint to set a watchpoint when stopped on the breakpoint.
+        lldbutil.run_break_set_by_symbol(self, 'main')
+
+        # Run the program.
+        self.runCmd("run", RUN_SUCCEEDED)
+
+        # We should be stopped due to the breakpoint.
+        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+                    substrs=['stopped',
+                             'stop reason = breakpoint'])
+
+        # Set the watchpoint variable declaration line number.
+        self.decl = line_number(self.source,
+                                '// Watchpoint variable declaration.')
+
+        # Now let's set a watchpoint on 'global_var'.
+        self.expect(
+            "watchpoint set variable global_var",
+            WATCHPOINT_CREATED,
+            substrs=[
+                'Watchpoint created',
+                'size = 4',
+                'type = w',
+                '%s:%d' %
+                (self.source,
+                 self.decl)])
+
+        self.verify_watch_hits()
+
+    @skipIf(archs=no_match(["aarch64"]))
+    @skipIf(oslist=no_match(['linux']))
+    def test_watch_set_on_tagged_ptr(self):
+        """Test that LLDB can install and hit watchpoint on a tagged address"""
+
+        if not self.isAArch64PAuth():
+            self.skipTest('Target must support pointer authentication.')
+
+        # Find the line number to break inside main().
+        self.line = line_number(self.source, '// Set break point at this line.')
+
+        # Add a breakpoint to set a watchpoint when stopped on the breakpoint.
+        lldbutil.run_break_set_by_file_and_line(
+            self, None, self.line, num_expected_locations=1)
+
+        # Run the program.
+        self.runCmd("run", RUN_SUCCEEDED)
+
+        # We should be stopped due to the breakpoint.
+        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+                    substrs=['stopped',
+                             'stop reason = breakpoint'])
+
+        # Now let's set a expression watchpoint on 'tagged_ptr'.
+        self.expect(
+            "watchpoint set expression -s 4 -- tagged_ptr",
+            WATCHPOINT_CREATED,
+            substrs=[
+                'Watchpoint created',
+                'size = 4',
+                'type = w'])
+
+        self.verify_watch_hits()
+
+    def verify_watch_hits(self):
+        # Use the '-v' option to do verbose listing of the watchpoint.
+        # The hit count should be 0 initially.
+        self.expect("watchpoint list -v",
+                    substrs=['Number of supported hardware watchpoints:',
+                             'hit_count = 0'])
+
+        self.runCmd("process continue")
+
+        # We should be stopped again due to the watchpoint (read_write type).
+        # The stop reason of the thread should be watchpoint.
+        self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT,
+                    substrs=['stop reason = watchpoint'])
+
+        self.runCmd("process continue")
+
+        # We should be stopped again due to the watchpoint (read_write type).
+        # The stop reason of the thread should be watchpoint.
+        self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT,
+                    substrs=['stop reason = watchpoint'])
+
+        self.runCmd("process continue")
+
+        # There should be no more watchpoint hit and the process status should
+        # be 'exited'.
+        self.expect("process status",
+                    substrs=['exited'])
+
+        # Use the '-v' option to do verbose listing of the watchpoint.
+        # The hit count should now be 2.
+        self.expect("watchpoint list -v",
+                    substrs=['hit_count = 2'])

diff  --git a/lldb/test/API/commands/watchpoints/watch_tagged_addr/main.c b/lldb/test/API/commands/watchpoints/watch_tagged_addr/main.c
new file mode 100644
index 000000000000..b005c2a0ca94
--- /dev/null
+++ b/lldb/test/API/commands/watchpoints/watch_tagged_addr/main.c
@@ -0,0 +1,29 @@
+#include <stdint.h>
+
+uint32_t global_var = 0; // Watchpoint variable declaration.
+
+int main(int argc, char **argv) {
+  int dummy = 0;
+  // Move address of global variable into tagged_ptr after tagging
+  // Simple tagging scheme where 62nd bit of tagged address is set
+  uint32_t *tagged_ptr = (uint32_t *)((uint64_t)&global_var | (1ULL << 62));
+
+  // pacdza computes and inserts a pointer authentication code for address
+  // stored in tagged_ptr using PAC key A.
+  __asm__ __volatile__("pacdza %0" : "=r"(tagged_ptr) : "r"(tagged_ptr));
+
+  ++dummy; // Set break point at this line.
+
+  // Increment global_var
+  ++global_var;
+
+  ++dummy;
+
+  // autdza authenticates tagged_ptr using PAC key A.
+  __asm__ __volatile__("autdza %0" : "=r"(tagged_ptr) : "r"(tagged_ptr));
+
+  // Increment global_var using tagged_ptr
+  ++*tagged_ptr;
+
+  return 0;
+}


        


More information about the lldb-commits mailing list