[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 28 09:24:12 PDT 2025
================
@@ -219,12 +224,66 @@ protected:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void operator^=(const __bitset& __v) _NOEXCEPT;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void flip() _NOEXCEPT;
+
+ // unsigned long spans only one word
+ template <typename _StorageType = __storage_type,
+ __enable_if_t<sizeof(_StorageType) >= sizeof(unsigned long), int> = 0>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long __to_ulong() const {
+ return static_cast<unsigned long>(__first_[0]);
+ }
+
+ // unsigned long may span multiple words which are concatenated to form the result
+ template <typename _StorageType = __storage_type,
+ __enable_if_t<sizeof(_StorageType) < sizeof(unsigned long), int> = 0>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long __to_ulong() const {
+ const size_t __ul_words = (sizeof(unsigned long) - 1) / sizeof(__storage_type) + 1;
+ const size_t __n_words = _N_words < __ul_words ? _N_words : __ul_words;
+ 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;
+ }
+
+ // _Bit_size > sizeof(unsigned long) * CHAR_BIT: overflow check needed
+ template <size_t _Bit_size = _Size, __enable_if_t<(_Bit_size > sizeof(unsigned long) * CHAR_BIT), int> = 0>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const {
+ if (auto __e = __make_iter(_Bit_size); std::find(__make_iter(sizeof(unsigned long) * CHAR_BIT), __e, true) != __e)
+ std::__throw_overflow_error("__bitset<_N_words, _Size>::__to_ulong overflow error");
+
+ return __to_ulong();
+ }
+
+ // _Bit_size <= sizeof(unsigned long) * CHAR_BIT: no overflow check needed
+ template <size_t _Bit_size = _Size, __enable_if_t<(_Bit_size <= sizeof(unsigned long) * CHAR_BIT), int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const {
- return to_ulong(integral_constant < bool, _Size< sizeof(unsigned long) * CHAR_BIT>());
+ return __to_ulong();
}
+
+# ifndef _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const {
- return to_ullong(integral_constant < bool, _Size< sizeof(unsigned long long) * CHAR_BIT>());
+ // Check for overflow if _Size does not fit in unsigned long long
+ if _LIBCPP_CONSTEXPR (_Size > sizeof(unsigned long long) * CHAR_BIT) {
----------------
ldionne wrote:
Since you're using `_LIBCPP_CONSTEXPR` here, I think the same code is going to work in C++03 and C++11 mode. It's just going to be a runtime `if` in C++03 mode, but that's fine.
We sometimes stop backporting APIs to older standard versions and document it with a release note. However, we tend to only do that when we have a good reason to, and I feel like this isn't one since the same code works in C++03 and C++11 mode without change. So in this case I'd leave the C++03 "extension" to avoid breaking people, at least until we have a better reason to drop it.
https://github.com/llvm/llvm-project/pull/121348
More information about the libcxx-commits
mailing list