Skip to content

Conversation

@tbaederr
Copy link
Contributor

When calling Block::movePointersTo(), the two blocks might have different metadata sizes, which causes the final pointer to be incorrect and point to garbage. Adjust the pointer base and offset accordingly.

Fixes #168018

When calling Block::movePointersTo(), the two blocks might have
different metadata sizes, which causes the final pointer to be incorrect
and point to garbage. Adjust the pointer base and offset accordingly.

Fixes llvm#168018
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:bytecode Issues for the clang bytecode constexpr interpreter labels Nov 14, 2025
@llvmbot
Copy link
Member

llvmbot commented Nov 14, 2025

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

Changes

When calling Block::movePointersTo(), the two blocks might have different metadata sizes, which causes the final pointer to be incorrect and point to garbage. Adjust the pointer base and offset accordingly.

Fixes #168018


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

4 Files Affected:

  • (modified) clang/lib/AST/ByteCode/InterpBlock.cpp (+9)
  • (modified) clang/lib/AST/ByteCode/MemberPointer.cpp (+10-1)
  • (modified) clang/lib/AST/ByteCode/Pointer.cpp (+1)
  • (modified) clang/test/AST/ByteCode/cxx11.cpp (+21)
diff --git a/clang/lib/AST/ByteCode/InterpBlock.cpp b/clang/lib/AST/ByteCode/InterpBlock.cpp
index 24825ad2557ef..dc0178afe1246 100644
--- a/clang/lib/AST/ByteCode/InterpBlock.cpp
+++ b/clang/lib/AST/ByteCode/InterpBlock.cpp
@@ -102,12 +102,21 @@ bool Block::hasPointer(const Pointer *P) const {
 
 void Block::movePointersTo(Block *B) {
   assert(B != this);
+  unsigned MDDiff = static_cast<int>(B->Desc->getMetadataSize()) -
+                    static_cast<int>(Desc->getMetadataSize());
 
   while (Pointers) {
     Pointer *P = Pointers;
 
     this->removePointer(P);
     P->BS.Pointee = B;
+
+    // If the metadata size changed between the two blocks, move the pointer
+    // base/offset. Realistically, this should only happen when we move pointers
+    // from a dummy pointer to a global one.
+    P->BS.Base += MDDiff;
+    P->Offset += MDDiff;
+
     B->addPointer(P);
   }
   assert(!this->hasPointers());
diff --git a/clang/lib/AST/ByteCode/MemberPointer.cpp b/clang/lib/AST/ByteCode/MemberPointer.cpp
index dfc8583e464ab..8b1b0187818e9 100644
--- a/clang/lib/AST/ByteCode/MemberPointer.cpp
+++ b/clang/lib/AST/ByteCode/MemberPointer.cpp
@@ -23,6 +23,15 @@ std::optional<Pointer> MemberPointer::toPointer(const Context &Ctx) const {
   if (!Base.isBlockPointer())
     return std::nullopt;
 
+  unsigned BlockMDSize = Base.block()->getDescriptor()->getMetadataSize();
+
+  if (PtrOffset >= 0) {
+    // If the resulting base would be too small, return nullopt.
+    if (Base.BS.Base < static_cast<unsigned>(PtrOffset) ||
+        (Base.BS.Base - PtrOffset < BlockMDSize))
+      return std::nullopt;
+  }
+
   Pointer CastedBase =
       (PtrOffset < 0 ? Base.atField(-PtrOffset) : Base.atFieldSub(PtrOffset));
 
@@ -31,7 +40,7 @@ std::optional<Pointer> MemberPointer::toPointer(const Context &Ctx) const {
     return std::nullopt;
 
   unsigned Offset = 0;
-  Offset += CastedBase.block()->getDescriptor()->getMetadataSize();
+  Offset += BlockMDSize;
 
   if (const auto *FD = dyn_cast<FieldDecl>(Dcl)) {
     if (FD->getParent() == BaseRecord->getDecl())
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp
index e417bdfb81b8f..25719bd6f0f91 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -33,6 +33,7 @@ Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset)
 Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset)
     : Offset(Offset), StorageKind(Storage::Block) {
   assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
+  assert(Base >= Pointee->getDescriptor()->getMetadataSize());
 
   BS = {Pointee, Base, nullptr, nullptr};
 
diff --git a/clang/test/AST/ByteCode/cxx11.cpp b/clang/test/AST/ByteCode/cxx11.cpp
index 95615350f5142..9a61321f67a40 100644
--- a/clang/test/AST/ByteCode/cxx11.cpp
+++ b/clang/test/AST/ByteCode/cxx11.cpp
@@ -398,3 +398,24 @@ namespace PointerCast {
                                           // expected-note {{cast that performs the conversions of a reinterpret_cast}}
   };
 }
+
+namespace DummyToGlobalBlockMove {
+  struct Baz {
+    unsigned int n;
+  };
+
+  struct AP {
+    const AP *p;
+    const Baz *lp;
+  };
+
+  class Bar {
+  public:
+    static Baz _m[];
+    static const AP m;
+  };
+
+  const AP Bar::m = {0, &Bar::_m[0]};
+  Baz Bar::_m[] = {{0}};
+  const AP m = {&Bar ::m};
+}

@tbaederr tbaederr merged commit dc3ae60 into llvm:main Nov 14, 2025
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:bytecode Issues for the clang bytecode constexpr interpreter clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[clang][bytecode] crash in clang::interp::collectBlocks

2 participants