[libcxx-commits] [libcxx] 4a652e4 - [libc++][hardening] Categorize more assertions.
Konstantin Varlamov via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jul 24 14:57:47 PDT 2023
Author: varconst
Date: 2023-07-24T14:57:01-07:00
New Revision: 4a652e4a996ff529f6da24097698dc679fd414df
URL: https://github.com/llvm/llvm-project/commit/4a652e4a996ff529f6da24097698dc679fd414df
DIFF: https://github.com/llvm/llvm-project/commit/4a652e4a996ff529f6da24097698dc679fd414df.diff
LOG: [libc++][hardening] Categorize more assertions.
Differential Revision: https://reviews.llvm.org/D155873
Added:
Modified:
libcxx/include/__config
libcxx/include/__hash_table
libcxx/include/__node_handle
libcxx/include/__stop_token/intrusive_list_view.h
libcxx/include/__string/char_traits.h
libcxx/include/__tree
libcxx/src/string.cpp
Removed:
################################################################################
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 8d793ad293a1fe..9759d3b9e8e062 100644
--- 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
index fb85efad025216..2ae7afdc10dea5 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -1109,7 +1109,7 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ typename __hash_table<_Tp, _Hash, _Equal, _Alloc>::iterator
__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 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::swap(__hash_table& __u)
_NOEXCEPT_(__is_nothrow_swappable<hasher>::value && __is_nothrow_swappable<key_equal>::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 @@ template <class _Tp, class _Hash, class _Equal, class _Alloc>
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
index 17a9589e2fbc80..cc4eaf73c0bbea 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -149,7 +149,7 @@ public:
_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
index 4b63955e0d13cc..11a3e267e7c6d6 100644
--- a/libcxx/include/__stop_token/intrusive_list_view.h
+++ b/libcxx/include/__stop_token/intrusive_list_view.h
@@ -67,7 +67,7 @@ struct __intrusive_list_view {
__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
index e42347488d37bb..c201fc9a1cda77 100644
--- a/libcxx/include/__string/char_traits.h
+++ b/libcxx/include/__string/char_traits.h
@@ -142,7 +142,8 @@ struct _LIBCPP_DEPRECATED_("char_traits<T> for T not equal to char, wchar_t, cha
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 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char>
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 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<wchar_t>
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 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char8_t>
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 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char16_t>
_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
index 92b5e65e115827..58d4a97c04035a 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -168,7 +168,7 @@ inline _LIBCPP_INLINE_VISIBILITY
_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 @@ inline _LIBCPP_INLINE_VISIBILITY
_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 @@ template <class _NodePtr>
_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 @@ inline _LIBCPP_INLINE_VISIBILITY
_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 @@ inline _LIBCPP_INLINE_VISIBILITY
_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 @@ template <class _NodePtr>
_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 @@ template <class _NodePtr>
_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 @@ template <class _NodePtr>
_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 @@ template <class _NodePtr>
_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 @@ template <class _NodePtr>
_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
index 3d671bccdc6e0b..4f3de555e3a903 100644
--- a/libcxx/src/string.cpp
+++ b/libcxx/src/string.cpp
@@ -346,7 +346,7 @@ S i_to_string(V v) {
constexpr size_t bufsize = numeric_limits<V>::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);
}
More information about the libcxx-commits
mailing list