[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:25 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 {
----------------
ldionne wrote:

I would try to refactor this into SFINAE, similarly to the ones below. This one's going to be more difficult because you have nested conditions but it might be possible to at least implement the first layer with SFINAE? I think that would make this easier to understand.

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


More information about the libcxx-commits mailing list