Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc++] Fix possible out of range access in bitset #121348

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 91 additions & 45 deletions libcxx/include/bitset
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,15 @@ 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>
# include <__cstddef/ptrdiff_t.h>
# include <__cstddef/size_t.h>
# include <__functional/hash.h>
# include <__functional/unary_function.h>
# include <__type_traits/integral_constant.h>
# include <__type_traits/is_char_like_type.h>
# include <climits>
# include <stdexcept>
Expand Down Expand Up @@ -218,10 +220,10 @@ 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 to_ulong(integral_constant < bool, _Size< sizeof(unsigned long) * CHAR_BIT>());
return __to_ulong(_BoolConstant < _Size< sizeof(unsigned long) * CHAR_BIT>());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current formatting is pretty confusing. I think clang-format will let you change the spacing around these brackets without complaining. I'd change to __to_ulong(_BoolConstant<(_Size < sizeof(unsigned long) * CHAR_BIT)>()).

}
_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>());
return __to_ullong(_BoolConstant < _Size< sizeof(unsigned long long) * CHAR_BIT>());
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool all() const _NOEXCEPT;
Expand All @@ -233,12 +235,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
_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 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;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong(true_type, true_type) const;
_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;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long __to_ullong(true_type, true_type) const;
};

template <size_t _N_words, size_t _Size>
Expand Down Expand Up @@ -283,21 +287,30 @@ inline _LIBCPP_HIDE_FROM_ABI void __bitset<_N_words, _Size>::__init(unsigned lon
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
# if (__SIZEOF_LONG_LONG__ + __SIZEOF_SIZE_T__ - 1) / __SIZEOF_SIZE_T__ == 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# if (__SIZEOF_LONG_LONG__ + __SIZEOF_SIZE_T__ - 1) / __SIZEOF_SIZE_T__ == 1
# if __SIZEOF_LONG_LONG__ <= __SIZEOF_SIZE_T__

: __first_{static_cast<__storage_type>(__v)}
# elif (__SIZEOF_LONG_LONG__ + __SIZEOF_SIZE_T__ - 1) / __SIZEOF_SIZE_T__ == 2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# elif (__SIZEOF_LONG_LONG__ + __SIZEOF_SIZE_T__ - 1) / __SIZEOF_SIZE_T__ == 2
# elif __SIZEOF_LONG_LONG__ <= 2 * __SIZEOF_SIZE_T__

and so on for the other ones below

: __first_{static_cast<__storage_type>(__v), static_cast<__storage_type>(__v >> __bits_per_word)}
# elif (__SIZEOF_LONG_LONG__ + __SIZEOF_SIZE_T__ - 1) / __SIZEOF_SIZE_T__ == 4
# if _N_words == 2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This #if is not going to work. This is a preprocessor #if but its argument is a template parameter. We'd need to use something like SFINAE instead to dispatch on the value of _N_words. And I guess this doesn't show up in the CI because these #ifs are never actually entered.

: __first_{static_cast<__storage_type>(__v), static_cast<__storage_type>(__v >> __bits_per_word)}
# elif _N_words == 3
: __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)}
static_cast<__storage_type>(__v >> __bits_per_word),
static_cast<__storage_type>(__v >> (__bits_per_word * 2))}
# else
: __first_{static_cast<__storage_type>(__v),
static_cast<__storage_type>(__v >> __bits_per_word),
static_cast<__storage_type>(__v >> (__bits_per_word * 2)),
static_cast<__storage_type>(__v >> (__bits_per_word * 3))}
# endif
# else
# error This constructor has not been ported to this platform
# endif
# endif
{
# ifdef _LIBCPP_CXX03_LANG
__init(__v, integral_constant<bool, sizeof(unsigned long long) == sizeof(__storage_type)>());
__init(__v, _BoolConstant<sizeof(unsigned long long) == sizeof(__storage_type)>());
# endif
}

Expand Down Expand Up @@ -338,52 +351,68 @@ _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 {
unsigned long __r = static_cast<unsigned long>(__first_[0]);
_LIBCPP_DIAGNOSTIC_PUSH
_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wshift-count-overflow")
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>
_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];
__bitset<_N_words, _Size>::__to_ullong(true_type, true_type) const {
unsigned long long __r = static_cast<unsigned long long>(__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);
const size_t __ull_wrods = (sizeof(unsigned long long) - 1) / sizeof(__storage_type) + 1;
const size_t __n_words = _N_words < __ull_wrods ? _N_words : __ull_wrods;
for (size_t __i = 1; __i < __n_words; ++__i)
__r |= static_cast<unsigned long long>(__first_[__i]) << (__bits_per_word * __i);
_LIBCPP_DIAGNOSTIC_POP
return __r;
}
Expand Down Expand Up @@ -483,15 +512,18 @@ protected:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool any() const _NOEXCEPT;

_LIBCPP_HIDE_FROM_ABI size_t __hash_code() const _NOEXCEPT;

private:
_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;
};

template <size_t _Size>
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_(_Size == __bits_per_word ? static_cast<__storage_type>(__v) : static_cast<__storage_type>(__v)) {}

template <size_t _Size>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void
Expand All @@ -518,12 +550,25 @@ 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 __to_ulong(_BoolConstant < _Size< sizeof(unsigned long) * CHAR_BIT>());
}

template <size_t _Size>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long __bitset<1, _Size>::__to_ulong(false_type) const {
if (auto __e = __make_iter(_Size); std::find(__make_iter(sizeof(unsigned long) * CHAR_BIT), __e, true) != __e)
__throw_overflow_error("__bitset<1, _Size>::__to_ulong overflow error");

return static_cast<unsigned long>(__first_);
}

template <size_t _Size>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long __bitset<1, _Size>::__to_ulong(true_type) const {
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>
Expand Down Expand Up @@ -587,8 +632,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; }
Expand Down Expand Up @@ -618,7 +663,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,
Expand Down