Skip to content

release/23.x: [lldb] Avoid returning a stale AddressOf (#212915) - #213377

Open
llvmbot wants to merge 1 commit into
llvm:release/23.xfrom
llvmbot:issue212915
Open

release/23.x: [lldb] Avoid returning a stale AddressOf (#212915)#213377
llvmbot wants to merge 1 commit into
llvm:release/23.xfrom
llvmbot:issue212915

Conversation

@llvmbot

@llvmbot llvmbot commented Jul 31, 2026

Copy link
Copy Markdown
Member

Backport 320164d

Requested by: @igorkudrin

The 'ValueObject::AddressOf()' method assumes that the address of a
value object cannot change, so when it is calculated once, it does not
need to be updated afterwards. However, this is not the case if the
'ValueObject' is a dependent object obtained by calling 'Dereference()'
of another 'ValueObject'. If the latter object is changed, the dependent
value object should return a new address from the 'AddressOf()' method
to reflect the change.

(cherry picked from commit 320164d)
@llvmbot

llvmbot commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

@jimingham What do you think about merging this PR to the release branch?

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-lldb

Author: llvmbot

Changes

Backport 320164d

Requested by: @igorkudrin


Full diff: https://github.com/llvm/llvm-project/pull/213377.diff

4 Files Affected:

  • (modified) lldb/source/ValueObject/ValueObject.cpp (+4-3)
  • (added) lldb/test/API/python_api/value/change_ptr/Makefile (+3)
  • (added) lldb/test/API/python_api/value/change_ptr/TestChangePtr.py (+65)
  • (added) lldb/test/API/python_api/value/change_ptr/main.c (+31)
diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp
index cac4933c64325..49fd513c7f578 100644
--- a/lldb/source/ValueObject/ValueObject.cpp
+++ b/lldb/source/ValueObject/ValueObject.cpp
@@ -2902,9 +2902,6 @@ ValueObjectSP ValueObject::Dereference(Status &error) {
 }
 
 ValueObjectSP ValueObject::AddressOf(Status &error) {
-  if (m_addr_of_valobj_sp)
-    return m_addr_of_valobj_sp;
-
   auto [addr, address_type] = GetAddressOf(/*scalar_is_load_address=*/false);
   error.Clear();
   if (addr != LLDB_INVALID_ADDRESS && address_type != eAddressTypeHost) {
@@ -2918,6 +2915,10 @@ ValueObjectSP ValueObject::AddressOf(Status &error) {
 
     case eAddressTypeFile:
     case eAddressTypeLoad: {
+      if (m_addr_of_valobj_sp &&
+          m_addr_of_valobj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS) == addr)
+        return m_addr_of_valobj_sp;
+      m_addr_of_valobj_sp.reset();
       CompilerType compiler_type = GetCompilerType();
       if (compiler_type) {
         std::string name(1, '&');
diff --git a/lldb/test/API/python_api/value/change_ptr/Makefile b/lldb/test/API/python_api/value/change_ptr/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/python_api/value/change_ptr/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/python_api/value/change_ptr/TestChangePtr.py b/lldb/test/API/python_api/value/change_ptr/TestChangePtr.py
new file mode 100644
index 0000000000000..f68eeb8404d70
--- /dev/null
+++ b/lldb/test/API/python_api/value/change_ptr/TestChangePtr.py
@@ -0,0 +1,65 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class ChangePtrTest(TestBase):
+    def test(self):
+        self.build()
+
+        src_file = lldb.SBFileSpec("main.c")
+        _, process, thread, _ = lldbutil.run_to_source_breakpoint(
+            self, "// break here 1", src_file
+        )
+
+        ## Test 1: The AddressOf of a dereferenced value should change when
+        ## the pointer value is updated.
+
+        frame = thread.GetFrameAtIndex(0)
+        p = frame.FindVariable("p")
+        deref = p.Dereference()
+        self.assertEqual(deref.GetValueAsUnsigned(), 5)
+        self.assertEqual(deref.AddressOf().GetValueAsUnsigned(), p.GetValueAsUnsigned())
+        thread.StepOver()
+        self.assertEqual(deref.GetValueAsUnsigned(), 7)
+        self.assertEqual(deref.AddressOf().GetValueAsUnsigned(), p.GetValueAsUnsigned())
+
+        ## Test 2: The AddressOf of a child value of a dereferenced value should
+        ## change when the base pointer updates.
+
+        lldbutil.continue_to_source_breakpoint(
+            self, process, "// break here 2", src_file
+        )
+        frame = thread.GetFrameAtIndex(0)
+        p = frame.FindVariable("p")
+        deref_child = p.Dereference().GetChildMemberWithName("b")
+        self.assertEqual(deref_child.GetValue(), "'b'")
+        self.assertEqual(
+            deref_child.AddressOf().GetValueAsUnsigned(), p.GetValueAsUnsigned() + 1
+        )
+        thread.StepOver()
+        self.assertEqual(deref_child.GetValue(), "'d'")
+        self.assertEqual(
+            deref_child.AddressOf().GetValueAsUnsigned(), p.GetValueAsUnsigned() + 1
+        )
+
+        ## Test 3: Verify AddressOf updates correctly with persistent expression results.
+        lldbutil.continue_to_source_breakpoint(
+            self, process, "// break here 3", src_file
+        )
+        frame = thread.GetFrameAtIndex(0)
+        frame.EvaluateExpression("int *$ptr = &a")
+        ptr = frame.FindValue("$ptr", lldb.eValueTypeConstResult)
+        deref = ptr.Dereference()
+        self.assertEqual(deref.GetValueAsUnsigned(), 5)
+        self.assertEqual(
+            deref.AddressOf().GetValueAsUnsigned(),
+            frame.FindVariable("a").AddressOf().GetValueAsUnsigned(),
+        )
+        frame.EvaluateExpression("$ptr = &b")
+        self.assertEqual(deref.GetValueAsUnsigned(), 7)
+        self.assertEqual(
+            deref.AddressOf().GetValueAsUnsigned(),
+            frame.FindVariable("b").AddressOf().GetValueAsUnsigned(),
+        )
diff --git a/lldb/test/API/python_api/value/change_ptr/main.c b/lldb/test/API/python_api/value/change_ptr/main.c
new file mode 100644
index 0000000000000..4cce83e61476c
--- /dev/null
+++ b/lldb/test/API/python_api/value/change_ptr/main.c
@@ -0,0 +1,31 @@
+int test1() {
+  int a = 5;
+  int b = 7;
+  int *p = &a;
+  p = &b; // break here 1
+  return *p;
+}
+
+char test2() {
+  struct S {
+    char a;
+    char b;
+  };
+  struct S arr[2] = {{'a', 'b'}, {'c', 'd'}};
+  struct S *p = arr;
+  ++p; // break here 2
+  return p->b;
+}
+
+int test3() {
+  int a = 5;
+  int b = 7;
+  return a + b; // break here 3
+}
+
+int main() {
+  test1();
+  test2();
+  test3();
+  return 0;
+}

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

🪟 Windows x64 Test Results

  • 33365 tests passed
  • 904 tests skipped

✅ The build succeeded and all tests passed.

@dyung dyung moved this from Needs Triage to Needs Review in LLVM Release Status Aug 4, 2026
@dyung

dyung commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Hi @JDevlieghere and @jimingham, if someone could take a few minutes to take a look at this change and give your approval if you think we should port this change to the release branch that would be appreciated. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Needs Review

Development

Successfully merging this pull request may close these issues.

3 participants