[libcxx-commits] [libcxx] [libc++] Add unsafe-buffer-usage attributes to span, vector, string and string_view (PR #119603)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Dec 11 10:12:52 PST 2024
https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/119603
This patch is a first step towards improving the integration of -Wunsafe-buffer-usage into the standard library. The patch basically flags common methods of span, vector & friends as unsafe when hardening is disabled.
This allows producing a warning diagnostic when the user explicitly opts-into those with -Wunsafe-buffer-usage AND hardening is disabled. For example, indexing a span with hardening disabled and the warning enabled will now produce:
<source>:644:11: warning: function introduces unsafe buffer manipulation [-Wunsafe-buffer-usage]
644 | (void)s[3];
| ^~~~
There are certainly more places in the library that could use this attribute and we can expand on it in the future, but this aims to cover the most important places and provide a direction for how to apply the same thing elsewhere.
Note that an explicit anti-goal of this patch is to set a precedent for marking arbitrary library APIs with this attribute. There's a large number of APIs in the library that can potentially access pointers in unsafe way, and we should be careful to consider where it actually makes sense to apply the attribute to make sure that these diagnostics stay relevant (for users) and maintainable (for libc++).
Fixes #107904
>From 72058ba5d4fa4575b9cee4b9e7893465eebdeb8c Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 10 Dec 2024 07:52:28 -0500
Subject: [PATCH] [libc++] Add unsafe-buffer-usage attributes to span, vector,
string and string_view
This patch is a first step towards improving the integration of
-Wunsafe-buffer-usage into the standard library. The patch basically
flags common methods of span, vector & friends as unsafe when hardening
is disabled.
This allows producing a warning diagnostic when the user explicitly
opts-into those with -Wunsafe-buffer-usage AND hardening is disabled.
For example, indexing a span with hardening disabled and the warning
enabled will now produce:
<source>:644:11: warning: function introduces unsafe buffer manipulation [-Wunsafe-buffer-usage]
644 | (void)s[3];
| ^~~~
There are certainly more places in the library that could use this
attribute and we can expand on it in the future, but this aims to
cover the most important places and provide a direction for how to
apply the same thing elsewhere.
Note that an explicit anti-goal of this patch is to set a precedent
for marking arbitrary library APIs with this attribute. There's a
large number of APIs in the library that can potentially access
pointers in unsafe way, and we should be careful to consider where
it actually makes sense to apply the attribute to make sure that these
diagnostics stay relevant (for users) and maintainable (for libc++).
Fixes #107904
---
libcxx/include/__assert | 6 ++
libcxx/include/__iterator/wrap_iter.h | 9 ++-
libcxx/include/__vector/vector.h | 25 ++++---
libcxx/include/span | 39 +++++++----
libcxx/include/string | 29 ++++----
libcxx/include/string_view | 15 ++--
.../vector/unsafe-buffer-usage.verify.cpp | 44 ++++++++++++
.../views.span/unsafe-buffer-usage.verify.cpp | 68 +++++++++++++++++++
.../unsafe-buffer-usage.verify.cpp | 41 +++++++++++
.../unsafe-buffer-usage.verify.cpp | 43 ++++++++++++
10 files changed, 275 insertions(+), 44 deletions(-)
create mode 100644 libcxx/test/libcxx/containers/sequences/vector/unsafe-buffer-usage.verify.cpp
create mode 100644 libcxx/test/libcxx/containers/views/views.span/unsafe-buffer-usage.verify.cpp
create mode 100644 libcxx/test/libcxx/strings/basic.string/unsafe-buffer-usage.verify.cpp
create mode 100644 libcxx/test/std/strings/string.view/unsafe-buffer-usage.verify.cpp
diff --git a/libcxx/include/__assert b/libcxx/include/__assert
index 90eaa6023587b9..18245c03bf4751 100644
--- a/libcxx/include/__assert
+++ b/libcxx/include/__assert
@@ -115,4 +115,10 @@
#endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_FAST
// clang-format on
+#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_NONE
+# define _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION [[_Clang::__unsafe_buffer_usage__]]
+#else
+# define _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION // those are checked
+#endif
+
#endif // _LIBCPP___ASSERT
diff --git a/libcxx/include/__iterator/wrap_iter.h b/libcxx/include/__iterator/wrap_iter.h
index 966c4675b7049a..cbac5054277bbe 100644
--- a/libcxx/include/__iterator/wrap_iter.h
+++ b/libcxx/include/__iterator/wrap_iter.h
@@ -10,6 +10,7 @@
#ifndef _LIBCPP___ITERATOR_WRAP_ITER_H
#define _LIBCPP___ITERATOR_WRAP_ITER_H
+#include <__assert>
#include <__compare/ordering.h>
#include <__compare/three_way_comparable.h>
#include <__config>
@@ -57,7 +58,10 @@ class __wrap_iter {
int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __wrap_iter(const __wrap_iter<_OtherIter>& __u) _NOEXCEPT
: __i_(__u.__i_) {}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 reference operator*() const _NOEXCEPT { return *__i_; }
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 reference
+ operator*() const _NOEXCEPT {
+ return *__i_;
+ }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pointer operator->() const _NOEXCEPT {
return std::__to_address(__i_);
}
@@ -96,7 +100,8 @@ class __wrap_iter {
*this += -__n;
return *this;
}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 reference operator[](difference_type __n) const _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 reference
+ operator[](difference_type __n) const _NOEXCEPT {
return __i_[__n];
}
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 6ba7ba7bcf724b..96207c3c48173a 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -391,11 +391,13 @@ class _LIBCPP_TEMPLATE_VIS vector {
//
// element access
//
- _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference operator[](size_type __n) _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference
+ operator[](size_type __n) _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n < size(), "vector[] index out of bounds");
return this->__begin_[__n];
}
- _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference operator[](size_type __n) const _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference
+ operator[](size_type __n) const _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n < size(), "vector[] index out of bounds");
return this->__begin_[__n];
}
@@ -410,19 +412,23 @@ class _LIBCPP_TEMPLATE_VIS vector {
return this->__begin_[__n];
}
- _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference front() _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference
+ front() _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "front() called on an empty vector");
return *this->__begin_;
}
- _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference front() const _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference
+ front() const _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "front() called on an empty vector");
return *this->__begin_;
}
- _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference back() _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference
+ back() _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "back() called on an empty vector");
return *(this->__end_ - 1);
}
- _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference back() const _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference
+ back() const _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "back() called on an empty vector");
return *(this->__end_ - 1);
}
@@ -462,7 +468,7 @@ class _LIBCPP_TEMPLATE_VIS vector {
}
#endif
- _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void pop_back() {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void pop_back() {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "vector::pop_back called on an empty vector");
this->__destruct_at_end(this->__end_ - 1);
}
@@ -1115,7 +1121,8 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 inline
}
template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::iterator
+_LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI
+typename vector<_Tp, _Allocator>::iterator
vector<_Tp, _Allocator>::erase(const_iterator __position) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
__position != end(), "vector::erase(iterator) called with a non-dereferenceable iterator");
@@ -1126,7 +1133,7 @@ vector<_Tp, _Allocator>::erase(const_iterator __position) {
}
template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
+_LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
vector<_Tp, _Allocator>::erase(const_iterator __first, const_iterator __last) {
_LIBCPP_ASSERT_VALID_INPUT_RANGE(__first <= __last, "vector::erase(first, last) called with invalid range");
pointer __p = this->__begin_ + (__first - begin());
diff --git a/libcxx/include/span b/libcxx/include/span
index 2d43d1d1079e44..34d8209f929c60 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -320,12 +320,14 @@ public:
return span<element_type, _Count>{data() + size() - _Count, _Count};
}
- _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent> first(size_type __count) const noexcept {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent>
+ first(size_type __count) const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count <= size(), "span<T, N>::first(count): count out of range");
return {data(), __count};
}
- _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent> last(size_type __count) const noexcept {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent>
+ last(size_type __count) const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count <= size(), "span<T, N>::last(count): count out of range");
return {data() + size() - __count, __count};
}
@@ -341,7 +343,7 @@ public:
return _ReturnType{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count};
}
- _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent>
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent>
subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__offset <= size(), "span<T, N>::subspan(offset, count): offset out of range");
if (__count == dynamic_extent)
@@ -355,7 +357,8 @@ public:
_LIBCPP_HIDE_FROM_ABI constexpr size_type size_bytes() const noexcept { return _Extent * sizeof(element_type); }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool empty() const noexcept { return _Extent == 0; }
- _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](size_type __idx) const noexcept {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr reference
+ operator[](size_type __idx) const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__idx < size(), "span<T, N>::operator[](index): index out of range");
return __data_[__idx];
}
@@ -368,12 +371,12 @@ public:
}
# endif
- _LIBCPP_HIDE_FROM_ABI constexpr reference front() const noexcept {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr reference front() const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "span<T, N>::front() on empty span");
return __data_[0];
}
- _LIBCPP_HIDE_FROM_ABI constexpr reference back() const noexcept {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr reference back() const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "span<T, N>::back() on empty span");
return __data_[size() - 1];
}
@@ -477,36 +480,41 @@ public:
: __data_{__other.data()}, __size_{__other.size()} {}
template <size_t _Count>
- _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count> first() const noexcept {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count>
+ first() const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Count <= size(), "span<T>::first<Count>(): Count out of range");
return span<element_type, _Count>{data(), _Count};
}
template <size_t _Count>
- _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count> last() const noexcept {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count>
+ last() const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Count <= size(), "span<T>::last<Count>(): Count out of range");
return span<element_type, _Count>{data() + size() - _Count, _Count};
}
- _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent> first(size_type __count) const noexcept {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent>
+ first(size_type __count) const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count <= size(), "span<T>::first(count): count out of range");
return {data(), __count};
}
- _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent> last(size_type __count) const noexcept {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent>
+ last(size_type __count) const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count <= size(), "span<T>::last(count): count out of range");
return {data() + size() - __count, __count};
}
template <size_t _Offset, size_t _Count = dynamic_extent>
- _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count> subspan() const noexcept {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count>
+ subspan() const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Offset <= size(), "span<T>::subspan<Offset, Count>(): Offset out of range");
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Count == dynamic_extent || _Count <= size() - _Offset,
"span<T>::subspan<Offset, Count>(): Offset + Count out of range");
return span<element_type, _Count>{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count};
}
- constexpr span<element_type, dynamic_extent> _LIBCPP_HIDE_FROM_ABI
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent>
subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__offset <= size(), "span<T>::subspan(offset, count): offset out of range");
if (__count == dynamic_extent)
@@ -520,7 +528,8 @@ public:
_LIBCPP_HIDE_FROM_ABI constexpr size_type size_bytes() const noexcept { return __size_ * sizeof(element_type); }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool empty() const noexcept { return __size_ == 0; }
- _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](size_type __idx) const noexcept {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr reference
+ operator[](size_type __idx) const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__idx < size(), "span<T>::operator[](index): index out of range");
return __data_[__idx];
}
@@ -533,12 +542,12 @@ public:
}
# endif
- _LIBCPP_HIDE_FROM_ABI constexpr reference front() const noexcept {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr reference front() const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "span<T>::front() on empty span");
return __data_[0];
}
- _LIBCPP_HIDE_FROM_ABI constexpr reference back() const noexcept {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr reference back() const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "span<T>::back() on empty span");
return __data_[size() - 1];
}
diff --git a/libcxx/include/string b/libcxx/include/string
index 17bf4b3b98bf34..d6a09951ad1a18 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1340,7 +1340,8 @@ public:
return size() == 0;
}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_reference operator[](size_type __pos) const _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_reference
+ operator[](size_type __pos) const _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__pos <= size(), "string index out of bounds");
if (__builtin_constant_p(__pos) && !__fits_in_sso(__pos)) {
return *(__get_long_pointer() + __pos);
@@ -1348,7 +1349,8 @@ public:
return *(data() + __pos);
}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference operator[](size_type __pos) _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference
+ operator[](size_type __pos) _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__pos <= size(), "string index out of bounds");
if (__builtin_constant_p(__pos) && !__fits_in_sso(__pos)) {
return *(__get_long_pointer() + __pos);
@@ -1446,24 +1448,31 @@ public:
# endif // _LIBCPP_CXX03_LANG
_LIBCPP_CONSTEXPR_SINCE_CXX20 void push_back(value_type __c);
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void pop_back();
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void pop_back() {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "string::pop_back(): string is already empty");
+ __erase_to_end(size() - 1);
+ }
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference front() _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference
+ front() _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "string::front(): string is empty");
return *__get_pointer();
}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_reference front() const _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_reference
+ front() const _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "string::front(): string is empty");
return *data();
}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference back() _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference
+ back() _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "string::back(): string is empty");
return *(__get_pointer() + size() - 1);
}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_reference back() const _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_reference
+ back() const _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "string::back(): string is empty");
return *(data() + size() - 1);
}
@@ -3311,12 +3320,6 @@ basic_string<_CharT, _Traits, _Allocator>::erase(const_iterator __first, const_i
return __b + static_cast<difference_type>(__r);
}
-template <class _CharT, class _Traits, class _Allocator>
-inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::pop_back() {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "string::pop_back(): string is already empty");
- __erase_to_end(size() - 1);
-}
-
template <class _CharT, class _Traits, class _Allocator>
inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::clear() _NOEXCEPT {
size_type __old_size = size();
diff --git a/libcxx/include/string_view b/libcxx/include/string_view
index 27b9f152ea290a..d58ed418fc470e 100644
--- a/libcxx/include/string_view
+++ b/libcxx/include/string_view
@@ -403,7 +403,8 @@ public:
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool empty() const _NOEXCEPT { return __size_ == 0; }
// [string.view.access], element access
- _LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI const_reference operator[](size_type __pos) const _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI const_reference
+ operator[](size_type __pos) const _NOEXCEPT {
return _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__pos < size(), "string_view[] index out of bounds"), __data_[__pos];
}
@@ -411,24 +412,28 @@ public:
return __pos >= size() ? (__throw_out_of_range("string_view::at"), __data_[0]) : __data_[__pos];
}
- _LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI const_reference front() const _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI const_reference
+ front() const _NOEXCEPT {
return _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "string_view::front(): string is empty"), __data_[0];
}
- _LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI const_reference back() const _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI const_reference
+ back() const _NOEXCEPT {
return _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "string_view::back(): string is empty"), __data_[__size_ - 1];
}
_LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI const_pointer data() const _NOEXCEPT { return __data_; }
// [string.view.modifiers], modifiers:
- _LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI void remove_prefix(size_type __n) _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI void
+ remove_prefix(size_type __n) _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n <= size(), "remove_prefix() can't remove more than size()");
__data_ += __n;
__size_ -= __n;
}
- _LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI void remove_suffix(size_type __n) _NOEXCEPT {
+ _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI void
+ remove_suffix(size_type __n) _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n <= size(), "remove_suffix() can't remove more than size()");
__size_ -= __n;
}
diff --git a/libcxx/test/libcxx/containers/sequences/vector/unsafe-buffer-usage.verify.cpp b/libcxx/test/libcxx/containers/sequences/vector/unsafe-buffer-usage.verify.cpp
new file mode 100644
index 00000000000000..b4fb7d8dd1185b
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/vector/unsafe-buffer-usage.verify.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: gcc
+
+// Make sure that std::vector's operations produce unsafe buffer access warnings when
+// -Wunsafe-buffer-usage is used, when hardening is disabled.
+//
+// Note: We disable _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER to ensure that the libc++
+// headers are considered system headers, to validate that users would get
+// those diagnostics.
+//
+// ADDITIONAL_COMPILE_FLAGS: -Wunsafe-buffer-usage -U_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
+// REQUIRES: libcpp-hardening-mode=none
+
+#include <vector>
+#include <cstddef>
+
+void f(std::vector<int> v, std::vector<int> const cv, std::size_t n) {
+ auto it = v.begin();
+
+ (void)v[n]; // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)cv[n]; // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)v.front(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)cv.front(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)v.back(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)cv.back(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ v.pop_back(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)v.erase(it); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)v.erase(it, it); // expected-warning {{function introduces unsafe buffer manipulation}}
+
+#if defined(_LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR)
+ (void)*it; // nothing
+ (void)it[n]; // nothing
+#else
+ (void)*it; // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)it[n]; // expected-warning {{function introduces unsafe buffer manipulation}}
+#endif
+}
diff --git a/libcxx/test/libcxx/containers/views/views.span/unsafe-buffer-usage.verify.cpp b/libcxx/test/libcxx/containers/views/views.span/unsafe-buffer-usage.verify.cpp
new file mode 100644
index 00000000000000..c0735a8098ccd7
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/views.span/unsafe-buffer-usage.verify.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: gcc
+
+// Make sure that std::span's operations produce unsafe buffer access warnings when
+// -Wunsafe-buffer-usage is used, when hardening is disabled.
+//
+// Note: We disable _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER to ensure that the libc++
+// headers are considered system headers, to validate that users would get
+// those diagnostics.
+//
+// ADDITIONAL_COMPILE_FLAGS: -Wunsafe-buffer-usage -U_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
+// REQUIRES: libcpp-hardening-mode=none
+
+#include <span>
+#include <cstddef>
+
+void f1(std::span<int, std::dynamic_extent> s, std::size_t n) {
+ (void)s.first<10>(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.first(n); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.last<10>(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.last(n); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.subspan<10, 20>(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.subspan<10>(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.subspan(n, n); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.subspan(n); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s[n]; // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.front(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.back(); // expected-warning {{function introduces unsafe buffer manipulation}}
+
+ auto it = s.begin();
+#ifdef _LIBCPP_ABI_BOUNDED_ITERATORS
+ (void)*it; // nothing
+ (void)it[n]; // nothing
+#else
+ (void)*it; // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)it[n]; // expected-warning {{function introduces unsafe buffer manipulation}}
+#endif
+}
+
+void f2(std::span<int, 1024> s, std::size_t n) {
+ (void)s.first<10>(); // nothing
+ (void)s.first(n); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.last<10>(); // nothing
+ (void)s.last(n); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.subspan<10, 20>(); // nothing
+ (void)s.subspan(n, n); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.subspan(n); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s[n]; // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.front(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.back(); // expected-warning {{function introduces unsafe buffer manipulation}}
+
+ auto it = s.begin();
+#ifdef _LIBCPP_ABI_BOUNDED_ITERATORS
+ (void)*it; // nothing
+ (void)it[n]; // nothing
+#else
+ (void)*it; // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)it[n]; // expected-warning {{function introduces unsafe buffer manipulation}}
+#endif
+}
diff --git a/libcxx/test/libcxx/strings/basic.string/unsafe-buffer-usage.verify.cpp b/libcxx/test/libcxx/strings/basic.string/unsafe-buffer-usage.verify.cpp
new file mode 100644
index 00000000000000..f8aa512e9643fd
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/unsafe-buffer-usage.verify.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: gcc
+
+// Make sure that std::string's operations produce unsafe buffer access warnings when
+// -Wunsafe-buffer-usage is used, when hardening is disabled.
+//
+// Note: We disable _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER to ensure that the libc++
+// headers are considered system headers, to validate that users would get
+// those diagnostics.
+//
+// ADDITIONAL_COMPILE_FLAGS: -Wunsafe-buffer-usage -U_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
+// REQUIRES: libcpp-hardening-mode=none
+
+#include <string>
+#include <cstddef>
+
+void f(std::string s, std::string const cs, std::size_t n) {
+ (void)s[n]; // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)cs[n]; // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.front(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)cs.front(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.back(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)cs.back(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ s.pop_back(); // expected-warning {{function introduces unsafe buffer manipulation}}
+
+ auto it = s.begin();
+#if defined(_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING)
+ (void)*it; // nothing
+ (void)it[n]; // nothing
+#else
+ (void)*it; // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)it[n]; // expected-warning {{function introduces unsafe buffer manipulation}}
+#endif
+}
diff --git a/libcxx/test/std/strings/string.view/unsafe-buffer-usage.verify.cpp b/libcxx/test/std/strings/string.view/unsafe-buffer-usage.verify.cpp
new file mode 100644
index 00000000000000..3d9333c7e219cf
--- /dev/null
+++ b/libcxx/test/std/strings/string.view/unsafe-buffer-usage.verify.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: gcc
+
+// Make sure that std::string_view's operations produce unsafe buffer access warnings when
+// -Wunsafe-buffer-usage is used, when hardening is disabled.
+//
+// Note: We disable _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER to ensure that the libc++
+// headers are considered system headers, to validate that users would get
+// those diagnostics.
+//
+// ADDITIONAL_COMPILE_FLAGS: -Wunsafe-buffer-usage -U_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
+// REQUIRES: libcpp-hardening-mode=none
+
+#include <string_view>
+#include <cstddef>
+
+void f(std::string_view s, std::size_t n) {
+ (void)s[n]; // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.front(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.back(); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.remove_prefix(n); // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)s.remove_suffix(n); // expected-warning {{function introduces unsafe buffer manipulation}}
+
+ auto it = s.begin();
+#if defined(_LIBCPP_ABI_BOUNDED_ITERATORS)
+ (void)*it; // nothing
+ (void)it[n]; // nothing
+#elif defined(_LIBCPP_ABI_USE_WRAP_ITER_IN_STD_STRING_VIEW)
+ (void)*it; // expected-warning {{function introduces unsafe buffer manipulation}}
+ (void)it[n]; // expected-warning {{function introduces unsafe buffer manipulation}}
+#else
+ (void)*it; // TODO: Why does this trigger nothing?
+ (void)it[n]; // expected-warning {{unsafe buffer access}}
+#endif
+}
More information about the libcxx-commits
mailing list