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

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed May 21 10:07:23 PDT 2025


================
@@ -338,53 +370,65 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void __bitset<_N_words, _Siz
 
 template <size_t _N_words, size_t _Size>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long
-__bitset<_N_words, _Size>::to_ulong(false_type) const {
-  __const_iterator __e = __make_iter(_Size);
-  __const_iterator __i = std::find(__make_iter(sizeof(unsigned long) * CHAR_BIT), __e, true);
-  if (__i != __e)
-    std::__throw_overflow_error("bitset to_ulong overflow error");
+__bitset<_N_words, _Size>::__to_ulong(false_type) const {
+  if (auto __e = __make_iter(_Size); std::find(__make_iter(sizeof(unsigned long) * CHAR_BIT), __e, true) != __e)
+    std::__throw_overflow_error("bitset __to_ulong overflow error");
+
+  return __to_ulong(true_type());
+}
 
-  return __first_[0];
+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 __to_ulong(true_type(), _BoolConstant<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) const {
-  return __first_[0];
+__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 {
+  const size_t __ul_wrods = (sizeof(unsigned long) - 1) / sizeof(__storage_type) + 1;
+  const size_t __n_words  = _N_words < __ul_wrods ? _N_words : __ul_wrods;
+  unsigned long __r       = static_cast<unsigned long>(__first_[0]);
+  for (size_t __i = 1; __i < __n_words; ++__i)
+    __r |= static_cast<unsigned long>(__first_[__i]) << (__bits_per_word * __i);
+  return __r;
 }
 
 template <size_t _N_words, size_t _Size>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long
-__bitset<_N_words, _Size>::to_ullong(false_type) const {
-  __const_iterator __e = __make_iter(_Size);
-  __const_iterator __i = std::find(__make_iter(sizeof(unsigned long long) * CHAR_BIT), __e, true);
-  if (__i != __e)
-    std::__throw_overflow_error("bitset to_ullong overflow error");
+__bitset<_N_words, _Size>::__to_ullong(false_type) const {
+  if (auto __e = __make_iter(_Size); std::find(__make_iter(sizeof(unsigned long long) * CHAR_BIT), __e, true) != __e)
+    std::__throw_overflow_error("bitset __to_ullong overflow error");
 
-  return to_ullong(true_type());
+  return __to_ullong(true_type());
 }
 
 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) const {
-  return to_ullong(true_type(), integral_constant<bool, sizeof(__storage_type) < sizeof(unsigned long long)>());
+__bitset<_N_words, _Size>::__to_ullong(true_type) const {
+  return __to_ullong(true_type(), _BoolConstant<sizeof(__storage_type) < sizeof(unsigned long long)>());
 }
 
 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];
+__bitset<_N_words, _Size>::__to_ullong(true_type, false_type) const {
+  return static_cast<unsigned long long>(__first_[0]);
 }
 
 template <size_t _N_words, size_t _Size>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long
-__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);
-  _LIBCPP_DIAGNOSTIC_POP
+__bitset<_N_words, _Size>::__to_ullong(true_type, true_type) const {
+  const size_t __ull_wrods = (sizeof(unsigned long long) - 1) / sizeof(__storage_type) + 1;
----------------
ldionne wrote:

```suggestion
  const size_t __ull_words = (sizeof(unsigned long long) - 1) / sizeof(__storage_type) + 1;
```

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


More information about the libcxx-commits mailing list