https://github.com/rjodinchr updated 
https://github.com/llvm/llvm-project/pull/238331

>From e84ead27e59e38649f1ea8c1cfd13b42dc7764a6 Mon Sep 17 00:00:00 2001
From: Romaric Jodin <[email protected]>
Date: Tue, 3 Nov 2026 11:51:54 +0100
Subject: [PATCH] [AST] Avoid casting tagged DeclGroup pointer to Decl* in
 DeclGroupRef

DeclGroupRef::getFromOpaquePtr(void *Ptr) previously converted the
opaque pointer directly via static_cast<Decl *>(Ptr) before storing it
in D, even when the low tag bit of Ptr is DeclGroupKind (0x1) and Ptr
actually points to a tagged DeclGroup allocation rather than a Decl.

Because Decl is a polymorphic class, building with Control Flow
Integrity (-fsanitize=cfi-derived-cast / -fsanitize=cfi-unrelated-cast)
instruments static_cast<Decl *>(Ptr) to verify the vtable pointer at
*Ptr. When Ptr is a tagged DeclGroup * (whose alignment is 8 bytes and
whose low bit is 1), dereferencing the unaligned tagged address as a
Decl * fails the CFI vtable check at runtime.

Inspect the tag bit in getFromOpaquePtr(void *Ptr) first and construct
the DeclGroupRef via DeclGroupRef(reinterpret_cast<DeclGroup *>(...))
when the kind is DeclGroupKind, only casting to Decl * when the pointer
actually represents a single Decl. Add a unit test in DeclTest covering
DeclGroupRef::getFromOpaquePtr round-trips for both single-decl and
multi-decl groups.
---
 clang/include/clang/AST/DeclGroup.h |  7 ++++---
 clang/unittests/AST/DeclTest.cpp    | 27 +++++++++++++++++++++++++++
 2 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/AST/DeclGroup.h 
b/clang/include/clang/AST/DeclGroup.h
index 6637599f0cd0c8..f6ba6aeedc8310 100644
--- a/clang/include/clang/AST/DeclGroup.h
+++ b/clang/include/clang/AST/DeclGroup.h
@@ -120,9 +120,10 @@ class DeclGroupRef {
 
   void *getAsOpaquePtr() const { return D; }
   static DeclGroupRef getFromOpaquePtr(void *Ptr) {
-    DeclGroupRef X;
-    X.D = static_cast<Decl*>(Ptr);
-    return X;
+    if ((reinterpret_cast<uintptr_t>(Ptr) & Mask) == SingleDeclKind)
+      return DeclGroupRef(static_cast<Decl *>(Ptr));
+    return DeclGroupRef(reinterpret_cast<DeclGroup *>(
+        reinterpret_cast<uintptr_t>(Ptr) & ~Mask));
   }
 };
 
diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp
index 195b8ab4c4e661..ad3cdeb91a1d14 100644
--- a/clang/unittests/AST/DeclTest.cpp
+++ b/clang/unittests/AST/DeclTest.cpp
@@ -1029,3 +1029,30 @@ TEST(Decl, ObjCPropertyDeclNameForDiagnostic) {
                              /*Qualified=*/true);
   EXPECT_EQ(ExtPQualifiedOS.str(), "-[MyClass extensionProp]");
 }
+
+TEST(Decl, DeclGroupRefOpaquePtrRoundTrip) {
+  auto AST = tooling::buildASTFromCode("int a, b;");
+  ASTContext &Ctx = AST->getASTContext();
+
+  auto *A = const_cast<VarDecl *>(
+      selectFirst<VarDecl>("a", match(varDecl(hasName("a")).bind("a"), Ctx)));
+  auto *B = const_cast<VarDecl *>(
+      selectFirst<VarDecl>("b", match(varDecl(hasName("b")).bind("b"), Ctx)));
+  ASSERT_NE(A, nullptr);
+  ASSERT_NE(B, nullptr);
+
+  DeclGroupRef Single(A);
+  DeclGroupRef SingleRoundTrip =
+      DeclGroupRef::getFromOpaquePtr(Single.getAsOpaquePtr());
+  EXPECT_TRUE(SingleRoundTrip.isSingleDecl());
+  EXPECT_EQ(SingleRoundTrip.getSingleDecl(), A);
+
+  Decl *Decls[] = {A, B};
+  DeclGroupRef Group = DeclGroupRef::Create(Ctx, Decls, 2);
+  DeclGroupRef GroupRoundTrip =
+      DeclGroupRef::getFromOpaquePtr(Group.getAsOpaquePtr());
+  ASSERT_TRUE(GroupRoundTrip.isDeclGroup());
+  ASSERT_EQ(std::distance(GroupRoundTrip.begin(), GroupRoundTrip.end()), 2);
+  EXPECT_EQ(GroupRoundTrip.begin()[0], A);
+  EXPECT_EQ(GroupRoundTrip.begin()[1], B);
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to