[clang] [clang][bytecode] Check composite bitcasts for indeterminate bits (PR #118988)
https://github.com/tbaederr closed https://github.com/llvm/llvm-project/pull/118988 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][bytecode] Check composite bitcasts for indeterminate bits (PR #118988)
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/118988 >From c45a6b422ff2ad9f60e7e39d335be2e3d3fe4465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= 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 | 33 ++ .../ByteCode/builtin-bit-cast-bitfields.cpp | 31 - 4 files changed, 82 insertions(+), 20 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 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..e26410c0b35e86 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp @@ -248,12 +248,10 @@ static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr, if (BitWidth.isZero()) return true; -if (!P.isInitialized()) { - assert(false && "Implement uninitialized value tracking"); - return ReturnOnUninit; -} +// Bits will be left uninitialized and diagnosed when reading. +if (!P.isInitialized()) + return true; -assert(P.isInitialized()); if (T == PT_Ptr) { assert(P.getType()->isNullPtrType()); // Clang treats nullptr_t has having NO bits in its value @@ -262,6 +260,7 @@ static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr, return true; } +assert(P.isInitialized()); auto Buff = std::make_unique(ObjectReprChars.getQuantity()); // Work around floating point types that contain unused padding bytes. @@ -355,10 +354,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 FullBi
[clang] [clang][bytecode] Check composite bitcasts for indeterminate bits (PR #118988)
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/118988 >From 7e723fd3ee3844ad83fa61118dd263a84a6145e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= 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 +++ clang/lib/AST/ByteCode/Interp.h | 5 +-- .../lib/AST/ByteCode/InterpBuiltinBitCast.cpp | 33 ++ .../ByteCode/builtin-bit-cast-bitfields.cpp | 31 - 5 files changed, 85 insertions(+), 22 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 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/Interp.h b/clang/lib/AST/ByteCode/Interp.h index c5c2a5ef19cc4d..01ea32fa3928b8 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -3043,8 +3043,9 @@ inline bool BitCast(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte, uint32_t ResultBitWidth, const llvm::fltSemantics *Sem) { const Pointer &FromPtr = S.Stk.pop(); - if (!CheckLoad(S, OpPC, FromPtr)) -return false; + // Only reject uninitialized global variables. + if (FromPtr.isStatic() && !FromPtr.isInitialized()) +return CheckInitialized(S, OpPC, FromPtr, AK_Read); size_t BuffSize = ResultBitWidth / 8; llvm::SmallVector Buff(BuffSize); diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp index 4c25a3bb132fcf..e26410c0b35e86 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp @@ -248,12 +248,10 @@ static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr, if (BitWidth.isZero()) return true; -if (!P.isInitialized()) { - assert(false && "Implement uninitialized value tracking"); - return ReturnOnUninit; -} +// Bits will be left uninitialized and diagnosed when reading. +if (!P.isInitialized()) + return true; -assert(P.isInitialized()); if (T == PT_Ptr) { assert(P.getType()->isNullPtrType()); // Clang treats nullptr_t has having NO bits in its val
[clang] [clang][bytecode] Check composite bitcasts for indeterminate bits (PR #118988)
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/118988 >From d3e02b47e352447647930cd1ca6142276e39b870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= 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 | 20 +-- .../ByteCode/builtin-bit-cast-bitfields.cpp | 15 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 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..aa5eab8335d0b7 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,21 @@ 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.Cur
[clang] [clang][bytecode] Check composite bitcasts for indeterminate bits (PR #118988)
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/118988 >From 08ff053907e66539afd497cf790141c285151958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= 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 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.Cu
[clang] [clang][bytecode] Check composite bitcasts for indeterminate bits (PR #118988)
llvmbot wrote: @llvm/pr-subscribers-clang Author: Timm Baeder (tbaederr) Changes --- 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 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
[clang] [clang][bytecode] Check composite bitcasts for indeterminate bits (PR #118988)
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?= 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 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