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

2024-12-07 Thread Timm Baeder via cfe-commits

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)

2024-12-07 Thread Timm Baeder via cfe-commits

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)

2024-12-06 Thread Timm Baeder via cfe-commits

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)

2024-12-06 Thread Timm Baeder via cfe-commits

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)

2024-12-06 Thread Timm Baeder via cfe-commits

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)

2024-12-06 Thread via cfe-commits

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)

2024-12-06 Thread Timm Baeder via cfe-commits

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