[clang] a8a0ffb - [clang][bytecode] Check source pointer for bitcast validity (#166907)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Nov 9 23:17:12 PST 2025
Author: Timm Baeder
Date: 2025-11-10T08:17:08+01:00
New Revision: a8a0ffba739d247e24faaf612ac8f2d8faf1de3c
URL: https://github.com/llvm/llvm-project/commit/a8a0ffba739d247e24faaf612ac8f2d8faf1de3c
DIFF: https://github.com/llvm/llvm-project/commit/a8a0ffba739d247e24faaf612ac8f2d8faf1de3c.diff
LOG: [clang][bytecode] Check source pointer for bitcast validity (#166907)
Unfortunately this is more dynamic than anticipated.
Fixes https://github.com/llvm/llvm-project/issues/165006
Added:
Modified:
clang/lib/AST/ByteCode/Compiler.cpp
clang/lib/AST/ByteCode/Compiler.h
clang/lib/AST/ByteCode/Interp.h
clang/lib/AST/ByteCode/Opcodes.td
clang/test/AST/ByteCode/cxx11.cpp
clang/test/AST/ByteCode/invalid.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 20836f663cdf8..f68422c6eb01d 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -208,19 +208,6 @@ template <class Emitter> class LocOverrideScope final {
} // namespace interp
} // namespace clang
-template <class Emitter>
-bool Compiler<Emitter>::isValidBitCast(const CastExpr *E) {
- QualType FromTy = E->getSubExpr()->getType()->getPointeeType();
- QualType ToTy = E->getType()->getPointeeType();
-
- if (classify(FromTy) == classify(ToTy))
- return true;
-
- if (FromTy->isVoidType() || ToTy->isVoidType())
- return true;
- return false;
-}
-
template <class Emitter>
bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
const Expr *SubExpr = CE->getSubExpr();
@@ -506,12 +493,9 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
if (!FromT || !ToT)
return false;
- if (!this->isValidBitCast(CE) &&
- !this->emitInvalidCast(CastKind::ReinterpretLike, /*Fatal=*/false, CE))
- return false;
-
assert(isPtrType(*FromT));
assert(isPtrType(*ToT));
+ bool SrcIsVoidPtr = SubExprTy->isVoidPointerType();
if (FromT == ToT) {
if (CE->getType()->isVoidPointerType() &&
!SubExprTy->isFunctionPointerType()) {
@@ -520,6 +504,10 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
if (!this->visit(SubExpr))
return false;
+ if (!this->emitCheckBitCast(CETy->getPointeeType().getTypePtr(),
+ SrcIsVoidPtr, CE))
+ return false;
+
if (CE->getType()->isFunctionPointerType() ||
SubExprTy->isFunctionPointerType()) {
return this->emitFnPtrCast(CE);
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index fac0a7f4e1886..5c46f75af4da3 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -425,8 +425,6 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
bool refersToUnion(const Expr *E);
- bool isValidBitCast(const CastExpr *E);
-
protected:
/// Variable to storage mapping.
llvm::DenseMap<const ValueDecl *, Scope::Local> Locals;
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index c16408cef1fde..cbd60c9f2b37c 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -3290,6 +3290,52 @@ inline bool SideEffect(InterpState &S, CodePtr OpPC) {
return S.noteSideEffect();
}
+inline bool CheckBitCast(InterpState &S, CodePtr OpPC, const Type *TargetType,
+ bool SrcIsVoidPtr) {
+ const auto &Ptr = S.Stk.peek<Pointer>();
+ if (Ptr.isZero())
+ return true;
+ if (!Ptr.isBlockPointer())
+ return true;
+
+ if (TargetType->isIntegerType())
+ return true;
+
+ if (SrcIsVoidPtr && S.getLangOpts().CPlusPlus) {
+ bool HasValidResult = !Ptr.isZero();
+
+ if (HasValidResult) {
+ if (S.getStdAllocatorCaller("allocate"))
+ return true;
+
+ const auto &E = cast<CastExpr>(S.Current->getExpr(OpPC));
+ if (S.getLangOpts().CPlusPlus26 &&
+ S.getASTContext().hasSimilarType(Ptr.getType(),
+ QualType(TargetType, 0)))
+ return true;
+
+ S.CCEDiag(E, diag::note_constexpr_invalid_void_star_cast)
+ << E->getSubExpr()->getType() << S.getLangOpts().CPlusPlus26
+ << Ptr.getType().getCanonicalType() << E->getType()->getPointeeType();
+ } else if (!S.getLangOpts().CPlusPlus26) {
+ const SourceInfo &E = S.Current->getSource(OpPC);
+ S.CCEDiag(E, diag::note_constexpr_invalid_cast)
+ << diag::ConstexprInvalidCastKind::CastFrom << "'void *'"
+ << S.Current->getRange(OpPC);
+ }
+ }
+
+ QualType PtrType = Ptr.getType();
+ if (PtrType->isRecordType() &&
+ PtrType->getAsRecordDecl() != TargetType->getAsRecordDecl()) {
+ S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_cast)
+ << diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
+ << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
+ return false;
+ }
+ return true;
+}
+
/// Same here, but only for casts.
inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind,
bool Fatal) {
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index a2eaa61ea4306..1785fcf4a7b20 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -422,8 +422,8 @@ def CheckLiteralType : Opcode {
}
def CheckArraySize : Opcode { let Args = [ArgUint64]; }
-
def CheckFunctionDecl : Opcode { let Args = [ArgFunctionDecl]; }
+def CheckBitCast : Opcode { let Args = [ArgTypePtr, ArgBool]; }
// [] -> [Value]
def GetGlobal : AccessOpcode;
diff --git a/clang/test/AST/ByteCode/cxx11.cpp b/clang/test/AST/ByteCode/cxx11.cpp
index 753e51dfbfc1c..95615350f5142 100644
--- a/clang/test/AST/ByteCode/cxx11.cpp
+++ b/clang/test/AST/ByteCode/cxx11.cpp
@@ -387,3 +387,14 @@ struct Counter {
// Passing an lvalue by value makes a non-elidable copy.
constexpr int PassByValue(Counter c) { return c.copies; }
static_assert(PassByValue(Counter(0)) == 0, "expect no copies");
+
+namespace PointerCast {
+ /// The two interpreters disagree here.
+ struct S { int x, y; } s;
+ constexpr S* sptr = &s;
+ struct U {};
+ struct Str {
+ int e : (Str*)(sptr) == (Str*)(sptr); // expected-error {{not an integral constant expression}} \
+ // expected-note {{cast that performs the conversions of a reinterpret_cast}}
+ };
+}
diff --git a/clang/test/AST/ByteCode/invalid.cpp b/clang/test/AST/ByteCode/invalid.cpp
index 1f2d6bc1d48eb..115c8663079a1 100644
--- a/clang/test/AST/ByteCode/invalid.cpp
+++ b/clang/test/AST/ByteCode/invalid.cpp
@@ -88,4 +88,22 @@ namespace InvalidBitCast {
// both-note {{in call to}}
+ struct sockaddr
+ {
+ char sa_data[8];
+ };
+ struct in_addr
+ {
+ unsigned int s_addr;
+ };
+ struct sockaddr_in
+ {
+ unsigned short int sin_port;
+ struct in_addr sin_addr;
+ };
+ /// Bitcast from sockaddr to sockaddr_in. Used to crash.
+ unsigned int get_addr(sockaddr addr) {
+ return ((sockaddr_in *)&addr)->sin_addr.s_addr;
+ }
+
}
More information about the cfe-commits
mailing list