From ee2fe7a71e6375d47a756dfd0198e459540c3ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= Date: Fri, 14 Nov 2025 14:11:33 +0100 Subject: [PATCH] [clang][bytecode] Adjust pointers when moving them 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 https://github.com/llvm/llvm-project/issues/168018 --- clang/lib/AST/ByteCode/InterpBlock.cpp | 9 +++++++++ clang/lib/AST/ByteCode/MemberPointer.cpp | 11 ++++++++++- clang/lib/AST/ByteCode/Pointer.cpp | 1 + clang/test/AST/ByteCode/cxx11.cpp | 21 +++++++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) 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(B->Desc->getMetadataSize()) - + static_cast(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 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(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 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(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}; +}