[llvm] [ADT] Fix FoldingSetIterator::operator* pointer adjustment for multiple inheritance (PR #219349)
Wenju He via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 27 21:58:25 PDT 2026
https://github.com/wenju-he updated https://github.com/llvm/llvm-project/pull/219349
>From e07695ad75c3e191fba907a45decf9bff1f297cd Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he at intel.com>
Date: Fri, 28 Aug 2026 04:50:57 +0200
Subject: [PATCH 1/4] [ADT] Fix FoldingSetIterator::operator* pointer
adjustment for multiple inheritance
b7dc8e356b89 replaced the void*->FoldingSetNode*->T* cast in
FoldingSetIteratorImpl::getNode() with a direct void*->T* cast,
dropping the base-to-derived adjustment needed when FoldingSetNode
isn't T's first base. Restore the FoldingSetNode* intermediate cast.
Found via a downstream user with a multiply-inherited FoldingSetNode
type that iterates its FoldingSet directly (via find_if over
begin()/end()), corrupting the dereferenced pointer and crashing.
Co-Authored-By: Claude Sonnet 5 <noreply at anthropic.com>
---
llvm/include/llvm/ADT/FoldingSet.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/include/llvm/ADT/FoldingSet.h b/llvm/include/llvm/ADT/FoldingSet.h
index 48c2467754074..036d782a51d7d 100644
--- a/llvm/include/llvm/ADT/FoldingSet.h
+++ b/llvm/include/llvm/ADT/FoldingSet.h
@@ -659,7 +659,7 @@ template <class T> class FoldingSetIterator : DebugEpochBase::HandleBase {
T &operator*() const {
assert(isHandleInSync() && "invalid iterator access!");
- return *static_cast<T *>(*Bucket);
+ return *static_cast<T *>(static_cast<FoldingSetNode *>(*Bucket));
}
T *operator->() const { return &operator*(); }
>From 2e0bf23173655e1a6811063da8dd373ee9b439ac Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he at intel.com>
Date: Fri, 28 Aug 2026 06:20:53 +0200
Subject: [PATCH 2/4] add unit test
Co-Authored-By: Claude Sonnet 5 <noreply at anthropic.com>
---
llvm/include/llvm/ADT/FoldingSet.h | 2 +-
llvm/unittests/ADT/FoldingSet.cpp | 17 +++++++++++++++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/llvm/include/llvm/ADT/FoldingSet.h b/llvm/include/llvm/ADT/FoldingSet.h
index 036d782a51d7d..48c2467754074 100644
--- a/llvm/include/llvm/ADT/FoldingSet.h
+++ b/llvm/include/llvm/ADT/FoldingSet.h
@@ -659,7 +659,7 @@ template <class T> class FoldingSetIterator : DebugEpochBase::HandleBase {
T &operator*() const {
assert(isHandleInSync() && "invalid iterator access!");
- return *static_cast<T *>(static_cast<FoldingSetNode *>(*Bucket));
+ return *static_cast<T *>(*Bucket);
}
T *operator->() const { return &operator*(); }
diff --git a/llvm/unittests/ADT/FoldingSet.cpp b/llvm/unittests/ADT/FoldingSet.cpp
index ba0b7326e26d4..f98577e5c370e 100644
--- a/llvm/unittests/ADT/FoldingSet.cpp
+++ b/llvm/unittests/ADT/FoldingSet.cpp
@@ -272,6 +272,23 @@ TEST(FoldingSetTest, Iterator) {
EXPECT_NE(It, ItCopy);
}
+// FoldingSetNode has a non-zero offset here, so operator* must adjust it.
+struct PolymorphicBase {
+ virtual ~PolymorphicBase() = default;
+};
+
+struct MultiplyInheritedNode : public PolymorphicBase, public FoldingSetNode {
+ void Profile(FoldingSetNodeID &ID) const { ID.AddInteger(0); }
+};
+
+TEST(FoldingSetTest, IteratorMultipleInheritance) {
+ FoldingSet<MultiplyInheritedNode> Set;
+ MultiplyInheritedNode N;
+ Set.InsertNode(&N);
+
+ EXPECT_EQ(&*Set.begin(), &N);
+}
+
TEST(FoldingSetTest, FoldingSetVectorBasic) {
FoldingSetVector<TrivialPair> Vec;
EXPECT_THAT(Vec, IsEmpty());
>From df000be3fd3fbc8cc173077115d1c9cd27cd7391 Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he at intel.com>
Date: Fri, 28 Aug 2026 06:22:11 +0200
Subject: [PATCH 3/4] revert revert FoldingSet.h
---
llvm/include/llvm/ADT/FoldingSet.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/include/llvm/ADT/FoldingSet.h b/llvm/include/llvm/ADT/FoldingSet.h
index 48c2467754074..036d782a51d7d 100644
--- a/llvm/include/llvm/ADT/FoldingSet.h
+++ b/llvm/include/llvm/ADT/FoldingSet.h
@@ -659,7 +659,7 @@ template <class T> class FoldingSetIterator : DebugEpochBase::HandleBase {
T &operator*() const {
assert(isHandleInSync() && "invalid iterator access!");
- return *static_cast<T *>(*Bucket);
+ return *static_cast<T *>(static_cast<FoldingSetNode *>(*Bucket));
}
T *operator->() const { return &operator*(); }
>From 76c6a8c9f888f404123cb7e3b8bf10b3faad6dc8 Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he at intel.com>
Date: Fri, 28 Aug 2026 06:54:09 +0200
Subject: [PATCH 4/4] address review comment: int Dummy = 0
---
llvm/unittests/ADT/FoldingSet.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/unittests/ADT/FoldingSet.cpp b/llvm/unittests/ADT/FoldingSet.cpp
index f98577e5c370e..2556d64bae7a3 100644
--- a/llvm/unittests/ADT/FoldingSet.cpp
+++ b/llvm/unittests/ADT/FoldingSet.cpp
@@ -273,11 +273,11 @@ TEST(FoldingSetTest, Iterator) {
}
// FoldingSetNode has a non-zero offset here, so operator* must adjust it.
-struct PolymorphicBase {
- virtual ~PolymorphicBase() = default;
+struct NonEmptyBase {
+ int Dummy = 0;
};
-struct MultiplyInheritedNode : public PolymorphicBase, public FoldingSetNode {
+struct MultiplyInheritedNode : public NonEmptyBase, public FoldingSetNode {
void Profile(FoldingSetNodeID &ID) const { ID.AddInteger(0); }
};
More information about the llvm-commits
mailing list