[libcxx-commits] [libcxx] [libc++] Fix possible out of range access in bitset::to_ullong implementation (PR #121348)

Peng Liu via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jan 1 16:28:24 PST 2025


https://github.com/winner245 updated https://github.com/llvm/llvm-project/pull/121348

>From 8d986e3f20a773676eb68e97d63fae859cae1f50 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Mon, 30 Dec 2024 12:19:19 -0500
Subject: [PATCH 1/3] Improve bitset::to_ullong Implementation

---
 libcxx/include/bitset | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/bitset b/libcxx/include/bitset
index 919d2a0f07e096..2f2129bb8cc767 100644
--- a/libcxx/include/bitset
+++ b/libcxx/include/bitset
@@ -133,6 +133,7 @@ template <size_t N> struct hash<std::bitset<N>>;
 #  include <__algorithm/fill.h>
 #  include <__algorithm/fill_n.h>
 #  include <__algorithm/find.h>
+#  include <__algorithm/min.h>
 #  include <__assert>
 #  include <__bit_reference>
 #  include <__config>
@@ -381,8 +382,9 @@ __bitset<_N_words, _Size>::to_ullong(true_type, true_type) const {
   unsigned long long __r = __first_[0];
   _LIBCPP_DIAGNOSTIC_PUSH
   _LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wshift-count-overflow")
-  for (size_t __i = 1; __i < sizeof(unsigned long long) / sizeof(__storage_type); ++__i)
-    __r |= static_cast<unsigned long long>(__first_[__i]) << (sizeof(__storage_type) * CHAR_BIT);
+  size_t __n_words = std::min<size_t>(_N_words, sizeof(unsigned long long) / sizeof(__storage_type));
+  for (size_t __i = 1; __i < __n_words; ++__i)
+    __r |= static_cast<unsigned long long>(__first_[__i]) << (sizeof(__storage_type) * CHAR_BIT * __i);
   _LIBCPP_DIAGNOSTIC_POP
   return __r;
 }

>From 5c636ec353f5de819e15c9f3460ecf5fa8eec291 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Tue, 31 Dec 2024 01:44:59 -0500
Subject: [PATCH 2/3] Make variable constant expression

---
 libcxx/include/bitset | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/bitset b/libcxx/include/bitset
index 2f2129bb8cc767..6ea53cde5670b3 100644
--- a/libcxx/include/bitset
+++ b/libcxx/include/bitset
@@ -382,7 +382,8 @@ __bitset<_N_words, _Size>::to_ullong(true_type, true_type) const {
   unsigned long long __r = __first_[0];
   _LIBCPP_DIAGNOSTIC_PUSH
   _LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wshift-count-overflow")
-  size_t __n_words = std::min<size_t>(_N_words, sizeof(unsigned long long) / sizeof(__storage_type));
+  const size_t __ull_words = sizeof(unsigned long long) / sizeof(__storage_type);
+  const size_t __n_words = _N_words < __ull_words ? _N_words : __ull_words;
   for (size_t __i = 1; __i < __n_words; ++__i)
     __r |= static_cast<unsigned long long>(__first_[__i]) << (sizeof(__storage_type) * CHAR_BIT * __i);
   _LIBCPP_DIAGNOSTIC_POP

>From afb4c9d7230f41178bff034c2ced60c7941e7b90 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Wed, 1 Jan 2025 18:06:08 -0500
Subject: [PATCH 3/3] Apply @frederick-vs-ja suggestions to support 16-bit
 platforms

---
 libcxx/include/bitset | 85 +++++++++++++++++++++++++++++++------------
 1 file changed, 61 insertions(+), 24 deletions(-)

diff --git a/libcxx/include/bitset b/libcxx/include/bitset
index 6ea53cde5670b3..c6b3a93237a05f 100644
--- a/libcxx/include/bitset
+++ b/libcxx/include/bitset
@@ -231,8 +231,14 @@ private:
   void __init(unsigned long long __v, false_type) _NOEXCEPT;
   _LIBCPP_HIDE_FROM_ABI void __init(unsigned long long __v, true_type) _NOEXCEPT;
 #  endif // _LIBCPP_CXX03_LANG
+#  if _LIBCPP_STD_VER == 11
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+  __initialize(unsigned long long __v, size_t __i, size_t __n_words) _NOEXCEPT;
+#  endif // _LIBCPP_STD_VER == 11
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong(false_type) const;
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong(true_type) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong(true_type, false_type) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong(true_type, true_type) const;
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong(false_type) const;
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong(true_type) const;
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong(true_type, false_type) const;
@@ -278,23 +284,34 @@ inline _LIBCPP_HIDE_FROM_ABI void __bitset<_N_words, _Size>::__init(unsigned lon
 
 #  endif // _LIBCPP_CXX03_LANG
 
+#  if _LIBCPP_STD_VER == 11
+template <size_t _N_words, size_t _Size>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+__bitset<_N_words, _Size>::__initialize(unsigned long long __v, size_t __i, size_t __n_words) _NOEXCEPT {
+  if (__i < __n_words) {
+    __first_[__i] = static_cast<__storage_type>(__v >> __bits_per_word * __i);
+    return __initialize(__v, __i + 1, __n_words);
+  }
+  return true;
+}
+#  endif
+
 template <size_t _N_words, size_t _Size>
 inline _LIBCPP_CONSTEXPR __bitset<_N_words, _Size>::__bitset(unsigned long long __v) _NOEXCEPT
 #  ifndef _LIBCPP_CXX03_LANG
-#    if __SIZEOF_SIZE_T__ == 8
-    : __first_{__v}
-#    elif __SIZEOF_SIZE_T__ == 4
-    : __first_{static_cast<__storage_type>(__v),
-               _Size >= 2 * __bits_per_word
-                   ? static_cast<__storage_type>(__v >> __bits_per_word)
-                   : static_cast<__storage_type>((__v >> __bits_per_word) &
-                                                 (__storage_type(1) << (_Size - __bits_per_word)) - 1)}
-#    else
-#      error This constructor has not been ported to this platform
-#    endif
+    : __first_{static_cast<__storage_type>(__v)}
 #  endif
 {
-#  ifdef _LIBCPP_CXX03_LANG
+#  ifndef _LIBCPP_CXX03_LANG
+  const size_t __ull_words = sizeof(unsigned long long) / sizeof(__storage_type);
+  const size_t __n_words   = _N_words < __ull_words ? _N_words : __ull_words;
+#    if _LIBCPP_STD_VER >= 14
+  for (size_t __i = 1; __i < __n_words; ++__i)
+    __first_[__i] = static_cast<__storage_type>(__v >> __bits_per_word * __i);
+#    else
+  (void)__initialize(__v, 1, __n_words);
+#    endif
+#  else
   __init(__v, integral_constant<bool, sizeof(unsigned long long) == sizeof(__storage_type)>());
 #  endif
 }
@@ -344,13 +361,33 @@ __bitset<_N_words, _Size>::to_ulong(false_type) const {
   if (__i != __e)
     __throw_overflow_error("bitset to_ulong overflow error");
 
-  return __first_[0];
+  return to_ulong(true_type());
 }
 
 template <size_t _N_words, size_t _Size>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long
 __bitset<_N_words, _Size>::to_ulong(true_type) const {
-  return __first_[0];
+  return to_ulong(true_type(), integral_constant<bool, sizeof(__storage_type) < sizeof(unsigned long)>());
+}
+
+template <size_t _N_words, size_t _Size>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long
+__bitset<_N_words, _Size>::to_ulong(true_type, false_type) const {
+  return static_cast<unsigned long>(__first_[0]);
+}
+
+template <size_t _N_words, size_t _Size>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long
+__bitset<_N_words, _Size>::to_ulong(true_type, true_type) const {
+  unsigned long __r = __first_[0];
+  _LIBCPP_DIAGNOSTIC_PUSH
+  _LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wshift-count-overflow")
+  const size_t __ul_words = sizeof(unsigned long) / sizeof(__storage_type);
+  const size_t __n_words  = _N_words < __ul_words ? _N_words : __ul_words;
+  for (size_t __i = 1; __i < __n_words; ++__i)
+    __r |= static_cast<unsigned long>(__first_[__i]) << (__bits_per_word * __i);
+  _LIBCPP_DIAGNOSTIC_POP
+  return __r;
 }
 
 template <size_t _N_words, size_t _Size>
@@ -373,7 +410,7 @@ __bitset<_N_words, _Size>::to_ullong(true_type) const {
 template <size_t _N_words, size_t _Size>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long
 __bitset<_N_words, _Size>::to_ullong(true_type, false_type) const {
-  return __first_[0];
+  return static_cast<unsigned long long>(__first_[0]);
 }
 
 template <size_t _N_words, size_t _Size>
@@ -383,9 +420,9 @@ __bitset<_N_words, _Size>::to_ullong(true_type, true_type) const {
   _LIBCPP_DIAGNOSTIC_PUSH
   _LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wshift-count-overflow")
   const size_t __ull_words = sizeof(unsigned long long) / sizeof(__storage_type);
-  const size_t __n_words = _N_words < __ull_words ? _N_words : __ull_words;
+  const size_t __n_words   = _N_words < __ull_words ? _N_words : __ull_words;
   for (size_t __i = 1; __i < __n_words; ++__i)
-    __r |= static_cast<unsigned long long>(__first_[__i]) << (sizeof(__storage_type) * CHAR_BIT * __i);
+    __r |= static_cast<unsigned long long>(__first_[__i]) << (__bits_per_word * __i);
   _LIBCPP_DIAGNOSTIC_POP
   return __r;
 }
@@ -494,8 +531,7 @@ inline _LIBCPP_CONSTEXPR __bitset<1, _Size>::__bitset() _NOEXCEPT : __first_(0)
 
 template <size_t _Size>
 inline _LIBCPP_CONSTEXPR __bitset<1, _Size>::__bitset(unsigned long long __v) _NOEXCEPT
-    : __first_(_Size == __bits_per_word ? static_cast<__storage_type>(__v)
-                                        : static_cast<__storage_type>(__v) & ((__storage_type(1) << _Size) - 1)) {}
+    : __first_(static_cast<__storage_type>(__v)) {}
 
 template <size_t _Size>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void
@@ -524,12 +560,12 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void __bitset<1, _Siz
 
 template <size_t _Size>
 inline _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long __bitset<1, _Size>::to_ulong() const {
-  return __first_;
+  return static_cast<unsigned long>(__first_);
 }
 
 template <size_t _Size>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long __bitset<1, _Size>::to_ullong() const {
-  return __first_;
+  return static_cast<unsigned long long>(__first_);
 }
 
 template <size_t _Size>
@@ -595,8 +631,8 @@ protected:
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void flip() _NOEXCEPT {}
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const { return 0; }
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const { return 0; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const { return 0UL; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const { return 0ULL; }
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool all() const _NOEXCEPT { return true; }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool any() const _NOEXCEPT { return false; }
@@ -626,7 +662,8 @@ public:
 
   // 23.3.5.1 constructors:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bitset() _NOEXCEPT {}
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bitset(unsigned long long __v) _NOEXCEPT : __base(__v) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bitset(unsigned long long __v) _NOEXCEPT
+      : __base(sizeof(unsigned long long) * CHAR_BIT <= _Size ? __v : __v & (1ULL << _Size) - 1) {}
   template <class _CharT, __enable_if_t<_IsCharLikeType<_CharT>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 explicit bitset(
       const _CharT* __str,



More information about the libcxx-commits mailing list