[clang] [clang][Interp] Bail out from type-punning casts (PR #163809)

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 16 08:50:34 PDT 2025


https://github.com/term-est created https://github.com/llvm/llvm-project/pull/163809

Fixes #163778 (fix might be indirect?)

Prevents emitting byte-code for UB casts

>From 927eeb016dd198237006440b49f59685f1c41a34 Mon Sep 17 00:00:00 2001
From: term-est <cancagri.dev at gmail.com>
Date: Thu, 16 Oct 2025 18:45:41 +0300
Subject: [PATCH] [clang][Interp] Bail out from type-punning casts

---
 clang/lib/AST/ByteCode/Compiler.cpp | 44 +++++++++++++++++++++++++++++
 clang/lib/AST/ByteCode/Compiler.h   |  2 ++
 2 files changed, 46 insertions(+)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 74cae030bb9bb..c30262dd8a323 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -212,6 +212,10 @@ template <class Emitter>
 bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
   const Expr *SubExpr = CE->getSubExpr();
 
+  if (isPunningDereference(SubExpr))
+    return this->emitInvalidCast(CastKind::Reinterpret, /*Fatal=*/true,
+                                 CE);
+
   if (DiscardResult)
     return this->delegate(SubExpr);
 
@@ -5511,6 +5515,46 @@ bool Compiler<Emitter>::maybeEmitDeferredVarInit(const VarDecl *VD) {
   return true;
 }
 
+template <class Emitter>
+bool Compiler<Emitter>::isPunningDereference(const Expr *E)
+{
+  E = E->IgnoreParenImpCasts();
+
+  const auto *UO = dyn_cast<UnaryOperator>(E);
+  if (!UO || UO->getOpcode() != UO_Deref)
+    return false;
+
+  const Expr *Base = UO->getSubExpr()->IgnoreParenImpCasts();
+  const auto *Cast = dyn_cast<CastExpr>(Base);
+  if (!Cast)
+    return false;
+
+  // Only consider reinterpret-ish casts
+  switch (Cast->getCastKind()) {
+    case CK_BitCast:
+    case CK_PointerToIntegral:
+    case CK_IntegralToPointer:
+    case CK_AddressSpaceConversion:
+      break;
+    default:
+      return false; // CK_NoOp etc. are fine
+  }
+
+  QualType DestPtrTy = Cast->getType();
+  QualType SrcPtrTy  = Cast->getSubExpr()->getType();
+  if (!DestPtrTy->isPointerType() || !SrcPtrTy->isPointerType())
+    return true; // super fishy, treat it as a pun
+
+  QualType DestPointee = DestPtrTy->getPointeeType();
+  QualType SrcPointee  = SrcPtrTy->getPointeeType();
+
+  // If pointee types differ (ignoring qualifiers), its a pun
+  if (!Ctx.getASTContext().hasSameUnqualifiedType(DestPointee, SrcPointee))
+    return true;
+
+  return false;
+}
+
 static bool hasTrivialDefaultCtorParent(const FieldDecl *FD) {
   assert(FD);
   assert(FD->getParent()->isUnion());
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 5c46f75af4da3..2e814fe3e623b 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -423,6 +423,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
   bool checkLiteralType(const Expr *E);
   bool maybeEmitDeferredVarInit(const VarDecl *VD);
 
+  bool isPunningDereference(const Expr *E);
+
   bool refersToUnion(const Expr *E);
 
 protected:



More information about the cfe-commits mailing list