Skip to content

[clang][bytecode] Check composite bitcasts for indeterminate bits #118988

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 7, 2024

Conversation

tbaederr
Copy link
Contributor

@tbaederr tbaederr commented Dec 6, 2024

No description provided.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Dec 6, 2024
@llvmbot
Copy link
Member

llvmbot commented Dec 6, 2024

@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 --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}}
 }

@tbaederr tbaederr force-pushed the builtin-bit-cast17 branch 3 times, most recently from d3e02b4 to 7e723fd Compare December 7, 2024 07:18
@tbaederr tbaederr merged commit b4150ed into llvm:main Dec 7, 2024
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants