diff --git a/libcxx/include/__config b/libcxx/include/__config --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -239,7 +239,6 @@ // - `_LIBCPP_ASSERT_VALID_INPUT_RANGE` -- checks that ranges (whether expressed as an iterator pair, an iterator and // a sentinel, an iterator and a count, or a `std::range`) given as input to library functions are valid: // - the sentinel is reachable from the begin iterator; -// - TODO(hardening): where applicable, the input range and the output range do not overlap; // - TODO(hardening): both iterators refer to the same container. // // - `_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS` -- checks that any attempts to access a container element, whether through @@ -247,6 +246,9 @@ // a non-existent element. For iterator checks to work, bounded iterators must be enabled in the ABI. Types like // `optional` and `function` are considered one-element containers for the purposes of this check. // +// - `_LIBCPP_ASSERT_NON_OVERLAPPING_RANGES` -- for functions that take several ranges as arguments, checks that the +// given ranges do not overlap. +// // - `_LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR` -- checks any operations that exchange nodes between containers to make sure // the containers have compatible allocators. // @@ -279,34 +281,39 @@ # if _LIBCPP_ENABLE_HARDENED_MODE // Enabled checks. -# define _LIBCPP_ASSERT_VALID_INPUT_RANGE(expression, message) _LIBCPP_ASSERT(expression, message) -# define _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(expression, message) _LIBCPP_ASSERT(expression, message) +# define _LIBCPP_ASSERT_VALID_INPUT_RANGE(expression, message) _LIBCPP_ASSERT(expression, message) +# define _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(expression, message) _LIBCPP_ASSERT(expression, message) // Disabled checks. -# define _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(expression, message) _LIBCPP_ASSUME(expression) -# define _LIBCPP_ASSERT_INTERNAL(expression, message) _LIBCPP_ASSUME(expression) -# define _LIBCPP_ASSERT_UNCATEGORIZED(expression, message) _LIBCPP_ASSUME(expression) +// Overlapping ranges will make algorithms produce incorrect results but don't directly lead to a security +// vulnerability. +# define _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(expression, message) _LIBCPP_ASSUME(expression) +# define _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(expression, message) _LIBCPP_ASSUME(expression) +# define _LIBCPP_ASSERT_INTERNAL(expression, message) _LIBCPP_ASSUME(expression) +# define _LIBCPP_ASSERT_UNCATEGORIZED(expression, message) _LIBCPP_ASSUME(expression) // Debug mode checks. # elif _LIBCPP_ENABLE_DEBUG_MODE // All checks enabled. -# define _LIBCPP_ASSERT_VALID_INPUT_RANGE(expression, message) _LIBCPP_ASSERT(expression, message) -# define _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(expression, message) _LIBCPP_ASSERT(expression, message) -# define _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(expression, message) _LIBCPP_ASSERT(expression, message) -# define _LIBCPP_ASSERT_INTERNAL(expression, message) _LIBCPP_ASSERT(expression, message) -# define _LIBCPP_ASSERT_UNCATEGORIZED(expression, message) _LIBCPP_ASSERT(expression, message) +# define _LIBCPP_ASSERT_VALID_INPUT_RANGE(expression, message) _LIBCPP_ASSERT(expression, message) +# define _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(expression, message) _LIBCPP_ASSERT(expression, message) +# define _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(expression, message) _LIBCPP_ASSERT(expression, message) +# define _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(expression, message) _LIBCPP_ASSERT(expression, message) +# define _LIBCPP_ASSERT_INTERNAL(expression, message) _LIBCPP_ASSERT(expression, message) +# define _LIBCPP_ASSERT_UNCATEGORIZED(expression, message) _LIBCPP_ASSERT(expression, message) // Disable all checks if hardening is not enabled. # else // All checks disabled. -# define _LIBCPP_ASSERT_VALID_INPUT_RANGE(expression, message) _LIBCPP_ASSUME(expression) -# define _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(expression, message) _LIBCPP_ASSUME(expression) -# define _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(expression, message) _LIBCPP_ASSUME(expression) -# define _LIBCPP_ASSERT_INTERNAL(expression, message) _LIBCPP_ASSUME(expression) -# define _LIBCPP_ASSERT_UNCATEGORIZED(expression, message) _LIBCPP_ASSUME(expression) +# define _LIBCPP_ASSERT_VALID_INPUT_RANGE(expression, message) _LIBCPP_ASSUME(expression) +# define _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(expression, message) _LIBCPP_ASSUME(expression) +# define _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(expression, message) _LIBCPP_ASSUME(expression) +# define _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(expression, message) _LIBCPP_ASSUME(expression) +# define _LIBCPP_ASSERT_INTERNAL(expression, message) _LIBCPP_ASSUME(expression) +# define _LIBCPP_ASSERT_UNCATEGORIZED(expression, message) _LIBCPP_ASSUME(expression) # endif // _LIBCPP_ENABLE_HARDENED_MODE // clang-format on diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table --- a/libcxx/include/__hash_table +++ b/libcxx/include/__hash_table @@ -1109,7 +1109,7 @@ local_iterator begin(size_type __n) { - _LIBCPP_ASSERT_UNCATEGORIZED(__n < bucket_count(), + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n < bucket_count(), "unordered container::begin(n) called with n >= bucket_count()"); return local_iterator(__bucket_list_[__n], __n, bucket_count()); } @@ -1118,7 +1118,7 @@ local_iterator end(size_type __n) { - _LIBCPP_ASSERT_UNCATEGORIZED(__n < bucket_count(), + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n < bucket_count(), "unordered container::end(n) called with n >= bucket_count()"); return local_iterator(nullptr, __n, bucket_count()); } @@ -1127,7 +1127,7 @@ const_local_iterator cbegin(size_type __n) const { - _LIBCPP_ASSERT_UNCATEGORIZED(__n < bucket_count(), + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n < bucket_count(), "unordered container::cbegin(n) called with n >= bucket_count()"); return const_local_iterator(__bucket_list_[__n], __n, bucket_count()); } @@ -1136,7 +1136,7 @@ const_local_iterator cend(size_type __n) const { - _LIBCPP_ASSERT_UNCATEGORIZED(__n < bucket_count(), + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n < bucket_count(), "unordered container::cend(n) called with n >= bucket_count()"); return const_local_iterator(nullptr, __n, bucket_count()); } @@ -2223,8 +2223,8 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::erase(const_iterator __p) { __next_pointer __np = __p.__node_; - _LIBCPP_ASSERT_UNCATEGORIZED(__p != end(), - "unordered container erase(iterator) called with a non-dereferenceable iterator"); + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__p != end(), + "unordered container::erase(iterator) called with a non-dereferenceable iterator"); iterator __r(__np); ++__r; remove(__p); @@ -2423,10 +2423,10 @@ _NOEXCEPT_(__is_nothrow_swappable::value && __is_nothrow_swappable::value) #endif { - _LIBCPP_ASSERT_UNCATEGORIZED(__node_traits::propagate_on_container_swap::value || - this->__node_alloc() == __u.__node_alloc(), - "list::swap: Either propagate_on_container_swap must be true" - " or the allocators must compare equal"); + _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__node_traits::propagate_on_container_swap::value || + this->__node_alloc() == __u.__node_alloc(), + "unordered container::swap: Either propagate_on_container_swap " + "must be true or the allocators must compare equal"); { __node_pointer_pointer __npp = __bucket_list_.release(); __bucket_list_.reset(__u.__bucket_list_.release()); @@ -2451,7 +2451,7 @@ typename __hash_table<_Tp, _Hash, _Equal, _Alloc>::size_type __hash_table<_Tp, _Hash, _Equal, _Alloc>::bucket_size(size_type __n) const { - _LIBCPP_ASSERT_UNCATEGORIZED(__n < bucket_count(), + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n < bucket_count(), "unordered container::bucket_size(n) called with n >= bucket_count()"); __next_pointer __np = __bucket_list_[__n]; size_type __bc = bucket_count(); diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle --- a/libcxx/include/__node_handle +++ b/libcxx/include/__node_handle @@ -149,7 +149,7 @@ _LIBCPP_INLINE_VISIBILITY __basic_node_handle& operator=(__basic_node_handle&& __other) { - _LIBCPP_ASSERT_UNCATEGORIZED( + _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR( __alloc_ == _VSTD::nullopt || __alloc_traits::propagate_on_container_move_assignment::value || __alloc_ == __other.__alloc_, diff --git a/libcxx/include/__stop_token/intrusive_list_view.h b/libcxx/include/__stop_token/intrusive_list_view.h --- a/libcxx/include/__stop_token/intrusive_list_view.h +++ b/libcxx/include/__stop_token/intrusive_list_view.h @@ -67,7 +67,7 @@ __node->__next_->__prev_ = __node->__prev_; } } else { - _LIBCPP_ASSERT_UNCATEGORIZED(__node == __head_, "Node to be removed has no prev node, so it has to be the head"); + _LIBCPP_ASSERT_INTERNAL(__node == __head_, "Node to be removed has no prev node, so it has to be the head"); __pop_front(); } } diff --git a/libcxx/include/__string/char_traits.h b/libcxx/include/__string/char_traits.h --- a/libcxx/include/__string/char_traits.h +++ b/libcxx/include/__string/char_traits.h @@ -142,7 +142,8 @@ static _LIBCPP_CONSTEXPR_SINCE_CXX20 char_type* copy(char_type* __s1, const char_type* __s2, size_t __n) { if (!__libcpp_is_constant_evaluated()) { - _LIBCPP_ASSERT_UNCATEGORIZED(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range"); + _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES( + __s2 < __s1 || __s2 >= __s1 + __n, "char_traits::copy overlapped range"); } char_type* __r = __s1; for (; __n; --__n, ++__s1, ++__s2) @@ -236,7 +237,8 @@ static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 char_type* copy(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT { if (!__libcpp_is_constant_evaluated()) - _LIBCPP_ASSERT_UNCATEGORIZED(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range"); + _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES( + __s2 < __s1 || __s2 >= __s1 + __n, "char_traits::copy overlapped range"); std::copy_n(__s2, __n, __s1); return __s1; } @@ -307,7 +309,8 @@ static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 char_type* copy(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT { if (!__libcpp_is_constant_evaluated()) - _LIBCPP_ASSERT_UNCATEGORIZED(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range"); + _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES( + __s2 < __s1 || __s2 >= __s1 + __n, "char_traits::copy overlapped range"); std::copy_n(__s2, __n, __s1); return __s1; } @@ -371,7 +374,8 @@ static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 char_type* copy(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT { if (!__libcpp_is_constant_evaluated()) - _LIBCPP_ASSERT_UNCATEGORIZED(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range"); + _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES( + __s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range"); std::copy_n(__s2, __n, __s1); return __s1; } @@ -455,7 +459,8 @@ _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 static char_type* copy(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT { if (!__libcpp_is_constant_evaluated()) - _LIBCPP_ASSERT_UNCATEGORIZED(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range"); + _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES( + __s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range"); std::copy_n(__s2, __n, __s1); return __s1; } diff --git a/libcxx/include/__tree b/libcxx/include/__tree --- a/libcxx/include/__tree +++ b/libcxx/include/__tree @@ -168,7 +168,7 @@ _NodePtr __tree_min(_NodePtr __x) _NOEXCEPT { - _LIBCPP_ASSERT_UNCATEGORIZED(__x != nullptr, "Root node shouldn't be null"); + _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Root node shouldn't be null"); while (__x->__left_ != nullptr) __x = __x->__left_; return __x; @@ -180,7 +180,7 @@ _NodePtr __tree_max(_NodePtr __x) _NOEXCEPT { - _LIBCPP_ASSERT_UNCATEGORIZED(__x != nullptr, "Root node shouldn't be null"); + _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Root node shouldn't be null"); while (__x->__right_ != nullptr) __x = __x->__right_; return __x; @@ -191,7 +191,7 @@ _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_next(_NodePtr __x) _NOEXCEPT { - _LIBCPP_ASSERT_UNCATEGORIZED(__x != nullptr, "node shouldn't be null"); + _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null"); if (__x->__right_ != nullptr) return _VSTD::__tree_min(__x->__right_); while (!_VSTD::__tree_is_left_child(__x)) @@ -204,7 +204,7 @@ _EndNodePtr __tree_next_iter(_NodePtr __x) _NOEXCEPT { - _LIBCPP_ASSERT_UNCATEGORIZED(__x != nullptr, "node shouldn't be null"); + _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null"); if (__x->__right_ != nullptr) return static_cast<_EndNodePtr>(_VSTD::__tree_min(__x->__right_)); while (!_VSTD::__tree_is_left_child(__x)) @@ -219,7 +219,7 @@ _NodePtr __tree_prev_iter(_EndNodePtr __x) _NOEXCEPT { - _LIBCPP_ASSERT_UNCATEGORIZED(__x != nullptr, "node shouldn't be null"); + _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null"); if (__x->__left_ != nullptr) return _VSTD::__tree_max(__x->__left_); _NodePtr __xx = static_cast<_NodePtr>(__x); @@ -233,7 +233,7 @@ _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_leaf(_NodePtr __x) _NOEXCEPT { - _LIBCPP_ASSERT_UNCATEGORIZED(__x != nullptr, "node shouldn't be null"); + _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null"); while (true) { if (__x->__left_ != nullptr) @@ -257,8 +257,8 @@ _LIBCPP_HIDE_FROM_ABI void __tree_left_rotate(_NodePtr __x) _NOEXCEPT { - _LIBCPP_ASSERT_UNCATEGORIZED(__x != nullptr, "node shouldn't be null"); - _LIBCPP_ASSERT_UNCATEGORIZED(__x->__right_ != nullptr, "node should have a right child"); + _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null"); + _LIBCPP_ASSERT_INTERNAL(__x->__right_ != nullptr, "node should have a right child"); _NodePtr __y = __x->__right_; __x->__right_ = __y->__left_; if (__x->__right_ != nullptr) @@ -278,8 +278,8 @@ _LIBCPP_HIDE_FROM_ABI void __tree_right_rotate(_NodePtr __x) _NOEXCEPT { - _LIBCPP_ASSERT_UNCATEGORIZED(__x != nullptr, "node shouldn't be null"); - _LIBCPP_ASSERT_UNCATEGORIZED(__x->__left_ != nullptr, "node should have a left child"); + _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null"); + _LIBCPP_ASSERT_INTERNAL(__x->__left_ != nullptr, "node should have a left child"); _NodePtr __y = __x->__left_; __x->__left_ = __y->__right_; if (__x->__left_ != nullptr) @@ -304,8 +304,8 @@ _LIBCPP_HIDE_FROM_ABI void __tree_balance_after_insert(_NodePtr __root, _NodePtr __x) _NOEXCEPT { - _LIBCPP_ASSERT_UNCATEGORIZED(__root != nullptr, "Root of the tree shouldn't be null"); - _LIBCPP_ASSERT_UNCATEGORIZED(__x != nullptr, "Can't attach null node to a leaf"); + _LIBCPP_ASSERT_INTERNAL(__root != nullptr, "Root of the tree shouldn't be null"); + _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Can't attach null node to a leaf"); __x->__is_black_ = __x == __root; while (__x != __root && !__x->__parent_unsafe()->__is_black_) { @@ -374,8 +374,8 @@ _LIBCPP_HIDE_FROM_ABI void __tree_remove(_NodePtr __root, _NodePtr __z) _NOEXCEPT { - _LIBCPP_ASSERT_UNCATEGORIZED(__root != nullptr, "Root node should not be null"); - _LIBCPP_ASSERT_UNCATEGORIZED(__z != nullptr, "The node to remove should not be null"); + _LIBCPP_ASSERT_INTERNAL(__root != nullptr, "Root node should not be null"); + _LIBCPP_ASSERT_INTERNAL(__z != nullptr, "The node to remove should not be null"); _LIBCPP_ASSERT_INTERNAL(std::__tree_invariant(__root), "The tree invariants should hold"); // __z will be removed from the tree. Client still needs to destruct/deallocate it // __y is either __z, or if __z has two children, __tree_next(__z). diff --git a/libcxx/src/string.cpp b/libcxx/src/string.cpp --- a/libcxx/src/string.cpp +++ b/libcxx/src/string.cpp @@ -346,7 +346,7 @@ constexpr size_t bufsize = numeric_limits::digits10 + 2; // +1 for minus, +1 for digits10 char buf[bufsize]; const auto res = to_chars(buf, buf + bufsize, v); - _LIBCPP_ASSERT_UNCATEGORIZED(res.ec == errc(), "bufsize must be large enough to accomodate the value"); + _LIBCPP_ASSERT_INTERNAL(res.ec == errc(), "bufsize must be large enough to accomodate the value"); return S(buf, res.ptr); }