[clang] [clang][bytecode] Check composite bitcasts for indeterminate bits (PR #118988)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Dec 6 07:23:55 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Timm Baeder (tbaederr)
<details>
<summary>Changes</summary>
---
Full diff: https://github.com/llvm/llvm-project/pull/118988.diff
4 Files Affected:
- (modified) clang/lib/AST/ByteCode/BitcastBuffer.cpp (+29-5)
- (modified) clang/lib/AST/ByteCode/BitcastBuffer.h (+4)
- (modified) clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp (+19-2)
- (modified) clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp (+14)
``````````diff
diff --git a/clang/lib/AST/ByteCode/BitcastBuffer.cpp b/clang/lib/AST/ByteCode/BitcastBuffer.cpp
index 7f29c7c2db0147..fbd500fd8f5f4d 100644
--- a/clang/lib/AST/ByteCode/BitcastBuffer.cpp
+++ b/clang/lib/AST/ByteCode/BitcastBuffer.cpp
@@ -62,11 +62,7 @@ BitcastBuffer::copyBits(Bits BitOffset, Bits BitWidth, Bits FullBitWidth,
}
bool BitcastBuffer::allInitialized() const {
- Bits Sum;
- for (BitRange BR : InitializedBits)
- Sum += BR.size();
-
- return Sum == FinalBitSize;
+ return rangeInitialized(Bits::zero(), FinalBitSize);
}
void BitcastBuffer::markInitialized(Bits Offset, Bits Length) {
@@ -111,6 +107,34 @@ void BitcastBuffer::markInitialized(Bits Offset, Bits Length) {
#endif
}
+bool BitcastBuffer::rangeInitialized(Bits Offset, Bits Length) const {
+ if (Length.isZero())
+ return true;
+
+ BitRange Range(Offset, Offset + Length - Bits(1));
+ Bits Sum;
+ bool FoundStart = false;
+ for (BitRange BR : InitializedBits) {
+ if (FoundStart) {
+ if (BR.contains(Range.End)) {
+ Sum += (Range.End - BR.Start + Bits(1));
+ break;
+ }
+
+ // Else, BR is completely inside Range.
+ Sum += BR.size();
+ }
+ if (BR.contains(Range.Start)) {
+ Sum += (BR.End - Range.Start + Bits(1));
+ FoundStart = true;
+ }
+ }
+
+ // Note that Sum can be larger than Range, e.g. when Range is fully
+ // contained in one range.
+ return Sum >= Range.size();
+}
+
#if 0
template<typename T>
static std::string hex(T t) {
diff --git a/clang/lib/AST/ByteCode/BitcastBuffer.h b/clang/lib/AST/ByteCode/BitcastBuffer.h
index 00fbdc9b85421d..2a0d8a0cd9a81f 100644
--- a/clang/lib/AST/ByteCode/BitcastBuffer.h
+++ b/clang/lib/AST/ByteCode/BitcastBuffer.h
@@ -55,6 +55,7 @@ struct Bytes {
Bits toBits() const { return Bits(N * 8); }
};
+/// A bit range. Both Start and End are inclusive.
struct BitRange {
Bits Start;
Bits End;
@@ -62,6 +63,8 @@ struct BitRange {
BitRange(Bits Start, Bits End) : Start(Start), End(End) {}
Bits size() const { return End - Start + Bits(1); }
bool operator<(BitRange Other) const { return Start.N < Other.Start.N; }
+
+ bool contains(Bits B) { return Start <= B && End >= B; }
};
/// Track what bits have been initialized to known values and which ones
@@ -85,6 +88,7 @@ struct BitcastBuffer {
/// Marks the bits in the given range as initialized.
/// FIXME: Can we do this automatically in pushData()?
void markInitialized(Bits Start, Bits Length);
+ bool rangeInitialized(Bits Offset, Bits Length) const;
/// Push \p BitWidth bits at \p BitOffset from \p In into the buffer.
/// \p TargetEndianness is the endianness of the target we're compiling for.
diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
index 4c25a3bb132fcf..dc5f0c6e6ab6ea 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
@@ -355,10 +355,11 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
ToPtr, S.getContext(), Buffer.size(),
[&](const Pointer &P, PrimType T, Bits BitOffset,
bool PackedBools) -> bool {
- CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(P.getType());
+ QualType PtrType = P.getType();
+ CharUnits ObjectReprChars = ASTCtx.getTypeSizeInChars(PtrType);
Bits FullBitWidth = Bits(ASTCtx.toBits(ObjectReprChars));
if (T == PT_Float) {
- const auto &Semantics = ASTCtx.getFloatTypeSemantics(P.getType());
+ const auto &Semantics = ASTCtx.getFloatTypeSemantics(PtrType);
Bits NumBits = Bits(llvm::APFloatBase::getSizeInBits(Semantics));
assert(NumBits.isFullByte());
assert(NumBits.getQuantity() <= FullBitWidth.getQuantity());
@@ -382,6 +383,22 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
else
BitWidth = FullBitWidth;
+ // If any of the bits are uninitialized, we need to abort unless the
+ // target type is std::byte or unsigned char.
+ if (!Buffer.rangeInitialized(BitOffset, BitWidth)) {
+ if (!PtrType->isStdByteType() &&
+ !PtrType->isSpecificBuiltinType(BuiltinType::UChar) &&
+ !PtrType->isSpecificBuiltinType(BuiltinType::Char_U)) {
+ const Expr *E = S.Current->getExpr(OpPC);
+ QualType ExprType = E->getType();
+ S.FFDiag(E, diag::note_constexpr_bit_cast_indet_dest)
+ << ExprType << S.getLangOpts().CharIsSigned
+ << E->getSourceRange();
+
+ return false;
+ }
+ }
+
auto Memory = Buffer.copyBits(BitOffset, BitWidth, FullBitWidth,
TargetEndianness);
if (llvm::sys::IsBigEndianHost)
diff --git a/clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp b/clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp
index e5337a57bf0fe4..7d6033d4bb1891 100644
--- a/clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp
+++ b/clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp
@@ -457,4 +457,18 @@ namespace IndeterminateBits {
};
constexpr unsigned char B = __builtin_bit_cast(unsigned char, S2{3});
static_assert(B == (LITTLE_END ? 3 : 192));
+
+
+
+ struct S {
+ unsigned a : 13;
+ unsigned : 17;
+ unsigned b : 2;
+ };
+
+ struct D {
+ unsigned a;
+ };
+ constexpr D s = __builtin_bit_cast(D, S{12, 3}); // expected-error {{must be initialized by a constant expression}} \
+ // expected-note {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'D' is invalid}}
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/118988
More information about the cfe-commits
mailing list