Skip to content

Commit 4ec9a20

Browse files
committed
[clang][bytecode] Check types when loading values
We need to allow BitCasts between pointer types to different prim types, but that means we need to catch the problem at a later stage, i.e. when loading the values. Fixes #158527 Fixex #163778
1 parent 046ed90 commit 4ec9a20

File tree

6 files changed

+80
-48
lines changed

6 files changed

+80
-48
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,19 @@ template <class Emitter> class LocOverrideScope final {
208208
} // namespace interp
209209
} // namespace clang
210210

211+
template <class Emitter>
212+
bool Compiler<Emitter>::isValidBitCast(const CastExpr *E) {
213+
QualType FromTy = E->getSubExpr()->getType()->getPointeeType();
214+
QualType ToTy = E->getType()->getPointeeType();
215+
216+
if (classify(FromTy) == classify(ToTy))
217+
return true;
218+
219+
if (FromTy->isVoidType() || ToTy->isVoidType())
220+
return true;
221+
return false;
222+
}
223+
211224
template <class Emitter>
212225
bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
213226
const Expr *SubExpr = CE->getSubExpr();
@@ -476,8 +489,9 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
476489
return this->delegate(SubExpr);
477490

478491
case CK_BitCast: {
492+
QualType CETy = CE->getType();
479493
// Reject bitcasts to atomic types.
480-
if (CE->getType()->isAtomicType()) {
494+
if (CETy->isAtomicType()) {
481495
if (!this->discard(SubExpr))
482496
return false;
483497
return this->emitInvalidCast(CastKind::Reinterpret, /*Fatal=*/true, CE);
@@ -492,6 +506,12 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
492506
if (!FromT || !ToT)
493507
return false;
494508

509+
if (!this->isValidBitCast(CE)) {
510+
if (!this->emitInvalidCast(CastKind::ReinterpretLike, /*Fatal=*/false,
511+
CE))
512+
return false;
513+
}
514+
495515
assert(isPtrType(*FromT));
496516
assert(isPtrType(*ToT));
497517
if (FromT == ToT) {

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
425425

426426
bool refersToUnion(const Expr *E);
427427

428+
bool isValidBitCast(const CastExpr *E);
429+
428430
protected:
429431
/// Variable to storage mapping.
430432
llvm::DenseMap<const ValueDecl *, Scope::Local> Locals;

clang/lib/AST/ByteCode/Interp.h

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,6 +1914,9 @@ bool Load(InterpState &S, CodePtr OpPC) {
19141914
return false;
19151915
if (!Ptr.isBlockPointer())
19161916
return false;
1917+
if (!Ptr.getFieldDesc()->isPrimitive() ||
1918+
Ptr.getFieldDesc()->getPrimType() != Name)
1919+
return false;
19171920
S.Stk.push<T>(Ptr.deref<T>());
19181921
return true;
19191922
}
@@ -1925,6 +1928,9 @@ bool LoadPop(InterpState &S, CodePtr OpPC) {
19251928
return false;
19261929
if (!Ptr.isBlockPointer())
19271930
return false;
1931+
if (!Ptr.getFieldDesc()->isPrimitive() ||
1932+
Ptr.getFieldDesc()->getPrimType() != Name)
1933+
return false;
19281934
S.Stk.push<T>(Ptr.deref<T>());
19291935
return true;
19301936
}
@@ -3286,12 +3292,18 @@ inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind,
32863292
bool Fatal) {
32873293
const SourceLocation &Loc = S.Current->getLocation(OpPC);
32883294

3289-
if (Kind == CastKind::Reinterpret) {
3295+
switch (Kind) {
3296+
case CastKind::Reinterpret:
32903297
S.CCEDiag(Loc, diag::note_constexpr_invalid_cast)
3291-
<< static_cast<unsigned>(Kind) << S.Current->getRange(OpPC);
3298+
<< diag::ConstexprInvalidCastKind::Reinterpret
3299+
<< S.Current->getRange(OpPC);
32923300
return !Fatal;
3293-
}
3294-
if (Kind == CastKind::Volatile) {
3301+
case CastKind::ReinterpretLike:
3302+
S.CCEDiag(Loc, diag::note_constexpr_invalid_cast)
3303+
<< diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
3304+
<< S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
3305+
return !Fatal;
3306+
case CastKind::Volatile:
32953307
if (!S.checkingPotentialConstantExpression()) {
32963308
const auto *E = cast<CastExpr>(S.Current->getExpr(OpPC));
32973309
if (S.getLangOpts().CPlusPlus)
@@ -3302,14 +3314,13 @@ inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind,
33023314
}
33033315

33043316
return false;
3305-
}
3306-
if (Kind == CastKind::Dynamic) {
3317+
case CastKind::Dynamic:
33073318
assert(!S.getLangOpts().CPlusPlus20);
3308-
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_cast)
3319+
S.CCEDiag(Loc, diag::note_constexpr_invalid_cast)
33093320
<< diag::ConstexprInvalidCastKind::Dynamic;
33103321
return true;
33113322
}
3312-
3323+
llvm_unreachable("Unhandled CastKind");
33133324
return false;
33143325
}
33153326

clang/lib/AST/ByteCode/PrimType.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ inline constexpr bool isSignedType(PrimType T) {
101101

102102
enum class CastKind : uint8_t {
103103
Reinterpret,
104+
ReinterpretLike,
104105
Volatile,
105106
Dynamic,
106107
};
@@ -111,6 +112,9 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
111112
case interp::CastKind::Reinterpret:
112113
OS << "reinterpret_cast";
113114
break;
115+
case interp::CastKind::ReinterpretLike:
116+
OS << "reinterpret_like";
117+
break;
114118
case interp::CastKind::Volatile:
115119
OS << "volatile";
116120
break;

clang/lib/AST/ByteCode/Program.cpp

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -36,30 +36,19 @@ unsigned Program::createGlobalString(const StringLiteral *S, const Expr *Base) {
3636
const size_t BitWidth = CharWidth * Ctx.getCharBit();
3737
unsigned StringLength = S->getLength();
3838

39-
PrimType CharType;
40-
switch (CharWidth) {
41-
case 1:
42-
CharType = PT_Sint8;
43-
break;
44-
case 2:
45-
CharType = PT_Uint16;
46-
break;
47-
case 4:
48-
CharType = PT_Uint32;
49-
break;
50-
default:
51-
llvm_unreachable("unsupported character width");
52-
}
39+
OptPrimType CharType =
40+
Ctx.classify(S->getType()->castAsArrayTypeUnsafe()->getElementType());
41+
assert(CharType);
5342

5443
if (!Base)
5544
Base = S;
5645

5746
// Create a descriptor for the string.
58-
Descriptor *Desc =
59-
allocateDescriptor(Base, CharType, Descriptor::GlobalMD, StringLength + 1,
60-
/*isConst=*/true,
61-
/*isTemporary=*/false,
62-
/*isMutable=*/false);
47+
Descriptor *Desc = allocateDescriptor(Base, *CharType, Descriptor::GlobalMD,
48+
StringLength + 1,
49+
/*isConst=*/true,
50+
/*isTemporary=*/false,
51+
/*isMutable=*/false);
6352

6453
// Allocate storage for the string.
6554
// The byte length does not include the null terminator.
@@ -79,26 +68,9 @@ unsigned Program::createGlobalString(const StringLiteral *S, const Expr *Base) {
7968
} else {
8069
// Construct the string in storage.
8170
for (unsigned I = 0; I <= StringLength; ++I) {
82-
const uint32_t CodePoint = I == StringLength ? 0 : S->getCodeUnit(I);
83-
switch (CharType) {
84-
case PT_Sint8: {
85-
using T = PrimConv<PT_Sint8>::T;
86-
Ptr.elem<T>(I) = T::from(CodePoint, BitWidth);
87-
break;
88-
}
89-
case PT_Uint16: {
90-
using T = PrimConv<PT_Uint16>::T;
91-
Ptr.elem<T>(I) = T::from(CodePoint, BitWidth);
92-
break;
93-
}
94-
case PT_Uint32: {
95-
using T = PrimConv<PT_Uint32>::T;
96-
Ptr.elem<T>(I) = T::from(CodePoint, BitWidth);
97-
break;
98-
}
99-
default:
100-
llvm_unreachable("unsupported character type");
101-
}
71+
uint32_t CodePoint = I == StringLength ? 0 : S->getCodeUnit(I);
72+
INT_TYPE_SWITCH_NO_BOOL(*CharType,
73+
Ptr.elem<T>(I) = T::from(CodePoint, BitWidth););
10274
}
10375
}
10476
Ptr.initializeAllElements();

clang/test/AST/ByteCode/invalid.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,26 @@ struct S {
6666
S s;
6767
S *sp[2] = {&s, &s};
6868
S *&spp = sp[1];
69+
70+
namespace InvalidBitCast {
71+
void foo() {
72+
const long long int i = 1; // both-note {{declared const here}}
73+
if (*(double *)&i == 2) {
74+
i = 0; // both-error {{cannot assign to variable}}
75+
}
76+
}
77+
78+
struct S2 {
79+
void *p;
80+
};
81+
struct T {
82+
S2 s;
83+
};
84+
constexpr T t = {{nullptr}};
85+
constexpr void *foo2() { return ((void **)&t)[0]; } // both-error {{never produces a constant expression}} \
86+
// both-note 2{{cast that performs the conversions of a reinterpret_cast}}
87+
constexpr auto x = foo2(); // both-error {{must be initialized by a constant expression}} \
88+
// both-note {{in call to}}
89+
90+
91+
}

0 commit comments

Comments
 (0)