https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/118988
None >From 0086686210e22c7e47444f2e46a262683d9c4594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Fri, 6 Dec 2024 15:52:38 +0100 Subject: [PATCH] [clang][bytecode] Check composite bitcasts for indeterminate bits --- clang/lib/AST/ByteCode/BitcastBuffer.cpp | 34 ++++++++++++++++--- clang/lib/AST/ByteCode/BitcastBuffer.h | 4 +++ .../lib/AST/ByteCode/InterpBuiltinBitCast.cpp | 21 ++++++++++-- .../ByteCode/builtin-bit-cast-bitfields.cpp | 14 ++++++++ 4 files changed, 66 insertions(+), 7 deletions(-) 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}} } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits