Skip to content

Commit b4150ed

Browse files
authored
[clang][bytecode] Check composite bitcasts for indeterminate bits (#118988)
1 parent 0cedb8f commit b4150ed

File tree

4 files changed

+82
-20
lines changed

4 files changed

+82
-20
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: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -248,12 +248,10 @@ static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
248248
if (BitWidth.isZero())
249249
return true;
250250

251-
if (!P.isInitialized()) {
252-
assert(false && "Implement uninitialized value tracking");
253-
return ReturnOnUninit;
254-
}
251+
// Bits will be left uninitialized and diagnosed when reading.
252+
if (!P.isInitialized())
253+
return true;
255254

256-
assert(P.isInitialized());
257255
if (T == PT_Ptr) {
258256
assert(P.getType()->isNullPtrType());
259257
// Clang treats nullptr_t has having NO bits in its value
@@ -262,6 +260,7 @@ static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
262260
return true;
263261
}
264262

263+
assert(P.isInitialized());
265264
auto Buff =
266265
std::make_unique<std::byte[]>(ObjectReprChars.getQuantity());
267266
// Work around floating point types that contain unused padding bytes.
@@ -355,10 +354,11 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
355354
ToPtr, S.getContext(), Buffer.size(),
356355
[&](const Pointer &P, PrimType T, Bits BitOffset,
357356
bool PackedBools) -> bool {
358-
CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
357+
QualType PtrType = P.getType();
358+
CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(PtrType);
359359
Bits FullBitWidth = Bits(ASTCtx.toBits(ObjectReprChars));
360360
if (T == PT_Float) {
361-
const auto &Semantics = ASTCtx.getFloatTypeSemantics(P.getType());
361+
const auto &Semantics = ASTCtx.getFloatTypeSemantics(PtrType);
362362
Bits NumBits = Bits(llvm::APFloatBase::getSizeInBits(Semantics));
363363
assert(NumBits.isFullByte());
364364
assert(NumBits.getQuantity() <= FullBitWidth.getQuantity());
@@ -382,6 +382,25 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
382382
else
383383
BitWidth = FullBitWidth;
384384

385+
// If any of the bits are uninitialized, we need to abort unless the
386+
// target type is std::byte or unsigned char.
387+
bool Initialized = Buffer.rangeInitialized(BitOffset, BitWidth);
388+
if (!Initialized) {
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+
llvm::errs() << "Not all initialized\n";
400+
return true;
401+
}
402+
llvm::errs() << "All initialized.\n";
403+
385404
auto Memory = Buffer.copyBits(BitOffset, BitWidth, FullBitWidth,
386405
TargetEndianness);
387406
if (llvm::sys::IsBigEndianHost)

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

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ struct bytes {
6363

6464
constexpr unsigned char operator[](size_t index) {
6565
if (index < N)
66-
return d[index];
66+
return d[index]; // expected-note {{read of uninitialized object}}
6767
return -1;
6868
}
6969
};
@@ -141,11 +141,11 @@ namespace BitFields {
141141
// expected-note {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'byte' is invalid}}
142142

143143
struct M {
144-
// ref-note@+1 {{subobject declared here}}
144+
// expected-note@+1 {{subobject declared here}}
145145
unsigned char mem[sizeof(BF)];
146146
};
147-
// ref-error@+2 {{initialized by a constant expression}}
148-
// ref-note@+1 {{not initialized}}
147+
// expected-error@+2 {{initialized by a constant expression}}
148+
// expected-note@+1 {{not initialized}}
149149
constexpr M m = bit_cast<M>(bf);
150150

151151
constexpr auto f = []() constexpr {
@@ -156,8 +156,8 @@ namespace BitFields {
156156

157157
static_assert(f()[0] + f()[1] + f()[2] == 0xc0 + 0xff + 0xee);
158158
{
159-
// ref-error@+2 {{initialized by a constant expression}}
160-
// ref-note@+1 {{read of uninitialized object is not allowed in a constant expression}}
159+
// expected-error@+2 {{initialized by a constant expression}}
160+
// expected-note@+1 {{in call to}}
161161
constexpr auto _bad = f()[3];
162162
}
163163

@@ -173,8 +173,8 @@ namespace BitFields {
173173
};
174174
static_assert(g().s0 + g().s1 + g().b0 + g().b1 == 0xc0 + 0xff + 0xe + 0xe);
175175
{
176-
// ref-error@+2 {{initialized by a constant expression}}
177-
// ref-note@+1 {{read of uninitialized object is not allowed in a constant expression}}
176+
// expected-error@+2 {{initialized by a constant expression}}
177+
// expected-note@+1 {{read of uninitialized object is not allowed in a constant expression}}
178178
constexpr auto _bad = g().b2;
179179
}
180180
}
@@ -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)