Skip to content

Commit d3e02b4

Browse files
committed
[clang][bytecode] Check composite bitcasts for indeterminate bits
1 parent 5fa59ed commit d3e02b4

File tree

4 files changed

+66
-7
lines changed

4 files changed

+66
-7
lines changed

clang/lib/AST/ByteCode/BitcastBuffer.cpp

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,7 @@ BitcastBuffer::copyBits(Bits BitOffset, Bits BitWidth, Bits FullBitWidth,
6262
}
6363

6464
bool BitcastBuffer::allInitialized() const {
65-
Bits Sum;
66-
for (BitRange BR : InitializedBits)
67-
Sum += BR.size();
68-
69-
return Sum == FinalBitSize;
65+
return rangeInitialized(Bits::zero(), FinalBitSize);
7066
}
7167

7268
void BitcastBuffer::markInitialized(Bits Offset, Bits Length) {
@@ -111,6 +107,34 @@ void BitcastBuffer::markInitialized(Bits Offset, Bits Length) {
111107
#endif
112108
}
113109

110+
bool BitcastBuffer::rangeInitialized(Bits Offset, Bits Length) const {
111+
if (Length.isZero())
112+
return true;
113+
114+
BitRange Range(Offset, Offset + Length - Bits(1));
115+
Bits Sum;
116+
bool FoundStart = false;
117+
for (BitRange BR : InitializedBits) {
118+
if (FoundStart) {
119+
if (BR.contains(Range.End)) {
120+
Sum += (Range.End - BR.Start + Bits(1));
121+
break;
122+
}
123+
124+
// Else, BR is completely inside Range.
125+
Sum += BR.size();
126+
}
127+
if (BR.contains(Range.Start)) {
128+
Sum += (BR.End - Range.Start + Bits(1));
129+
FoundStart = true;
130+
}
131+
}
132+
133+
// Note that Sum can be larger than Range, e.g. when Range is fully
134+
// contained in one range.
135+
return Sum >= Range.size();
136+
}
137+
114138
#if 0
115139
template<typename T>
116140
static std::string hex(T t) {

clang/lib/AST/ByteCode/BitcastBuffer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,16 @@ struct Bytes {
5555
Bits toBits() const { return Bits(N * 8); }
5656
};
5757

58+
/// A bit range. Both Start and End are inclusive.
5859
struct BitRange {
5960
Bits Start;
6061
Bits End;
6162

6263
BitRange(Bits Start, Bits End) : Start(Start), End(End) {}
6364
Bits size() const { return End - Start + Bits(1); }
6465
bool operator<(BitRange Other) const { return Start.N < Other.Start.N; }
66+
67+
bool contains(Bits B) { return Start <= B && End >= B; }
6568
};
6669

6770
/// Track what bits have been initialized to known values and which ones
@@ -85,6 +88,7 @@ struct BitcastBuffer {
8588
/// Marks the bits in the given range as initialized.
8689
/// FIXME: Can we do this automatically in pushData()?
8790
void markInitialized(Bits Start, Bits Length);
91+
bool rangeInitialized(Bits Offset, Bits Length) const;
8892

8993
/// Push \p BitWidth bits at \p BitOffset from \p In into the buffer.
9094
/// \p TargetEndianness is the endianness of the target we're compiling for.

clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,10 +355,11 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
355355
ToPtr, S.getContext(), Buffer.size(),
356356
[&](const Pointer &P, PrimType T, Bits BitOffset,
357357
bool PackedBools) -> bool {
358-
CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
358+
QualType PtrType = P.getType();
359+
CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(PtrType);
359360
Bits FullBitWidth = Bits(ASTCtx.toBits(ObjectReprChars));
360361
if (T == PT_Float) {
361-
const auto &Semantics = ASTCtx.getFloatTypeSemantics(P.getType());
362+
const auto &Semantics = ASTCtx.getFloatTypeSemantics(PtrType);
362363
Bits NumBits = Bits(llvm::APFloatBase::getSizeInBits(Semantics));
363364
assert(NumBits.isFullByte());
364365
assert(NumBits.getQuantity() <= FullBitWidth.getQuantity());
@@ -382,6 +383,21 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
382383
else
383384
BitWidth = FullBitWidth;
384385

386+
// If any of the bits are uninitialized, we need to abort unless the
387+
// target type is std::byte or unsigned char.
388+
if (!Buffer.rangeInitialized(BitOffset, BitWidth)) {
389+
if (!PtrType->isStdByteType() &&
390+
!PtrType->isSpecificBuiltinType(BuiltinType::UChar) &&
391+
!PtrType->isSpecificBuiltinType(BuiltinType::Char_U)) {
392+
const Expr *E = S.Current->getExpr(OpPC);
393+
S.FFDiag(E, diag::note_constexpr_bit_cast_indet_dest)
394+
<< PtrType << S.getLangOpts().CharIsSigned
395+
<< E->getSourceRange();
396+
397+
return false;
398+
}
399+
}
400+
385401
auto Memory = Buffer.copyBits(BitOffset, BitWidth, FullBitWidth,
386402
TargetEndianness);
387403
if (llvm::sys::IsBigEndianHost)

clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,4 +457,19 @@ namespace IndeterminateBits {
457457
};
458458
constexpr unsigned char B = __builtin_bit_cast(unsigned char, S2{3});
459459
static_assert(B == (LITTLE_END ? 3 : 192));
460+
461+
462+
463+
struct S3 {
464+
unsigned a : 13;
465+
unsigned : 17;
466+
unsigned b : 2;
467+
};
468+
469+
struct D {
470+
unsigned a;
471+
};
472+
constexpr D s = __builtin_bit_cast(D, S3{12, 3}); // expected-error {{must be initialized by a constant expression}} \
473+
// expected-note {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'unsigned int' is invalid}}
474+
460475
}

0 commit comments

Comments
 (0)