[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