From 72058ba5d4fa4575b9cee4b9e7893465eebdeb8c Mon Sep 17 00:00:00 2001 From: Louis Dionne 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: :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 90eaa6023587b..18245c03bf475 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 966c4675b7049..cbac5054277bb 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 6ba7ba7bcf724..96207c3c48173 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 -_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 -_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 2d43d1d1079e4..34d8209f929c6 100644 --- a/libcxx/include/span +++ b/libcxx/include/span @@ -320,12 +320,14 @@ public: return span{data() + size() - _Count, _Count}; } - _LIBCPP_HIDE_FROM_ABI constexpr span first(size_type __count) const noexcept { + _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span + first(size_type __count) const noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count <= size(), "span::first(count): count out of range"); return {data(), __count}; } - _LIBCPP_HIDE_FROM_ABI constexpr span last(size_type __count) const noexcept { + _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span + last(size_type __count) const noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count <= size(), "span::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 + _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__offset <= size(), "span::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::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::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::back() on empty span"); return __data_[size() - 1]; } @@ -477,36 +480,41 @@ public: : __data_{__other.data()}, __size_{__other.size()} {} template - _LIBCPP_HIDE_FROM_ABI constexpr span first() const noexcept { + _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span + first() const noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Count <= size(), "span::first(): Count out of range"); return span{data(), _Count}; } template - _LIBCPP_HIDE_FROM_ABI constexpr span last() const noexcept { + _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span + last() const noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Count <= size(), "span::last(): Count out of range"); return span{data() + size() - _Count, _Count}; } - _LIBCPP_HIDE_FROM_ABI constexpr span first(size_type __count) const noexcept { + _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span + first(size_type __count) const noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count <= size(), "span::first(count): count out of range"); return {data(), __count}; } - _LIBCPP_HIDE_FROM_ABI constexpr span last(size_type __count) const noexcept { + _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span + last(size_type __count) const noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count <= size(), "span::last(count): count out of range"); return {data() + size() - __count, __count}; } template - _LIBCPP_HIDE_FROM_ABI constexpr span subspan() const noexcept { + _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span + subspan() const noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Offset <= size(), "span::subspan(): Offset out of range"); _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Count == dynamic_extent || _Count <= size() - _Offset, "span::subspan(): Offset + Count out of range"); return span{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count}; } - constexpr span _LIBCPP_HIDE_FROM_ABI + _LIBCPP_VALID_ELEMENT_ACCESS_PRECONDITION _LIBCPP_HIDE_FROM_ABI constexpr span subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__offset <= size(), "span::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::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::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::back() on empty span"); return __data_[size() - 1]; } diff --git a/libcxx/include/string b/libcxx/include/string index 17bf4b3b98bf3..d6a09951ad1a1 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(__r); } -template -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 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 27b9f152ea290..d58ed418fc470 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 0000000000000..b4fb7d8dd1185 --- /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 +#include + +void f(std::vector v, std::vector 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 0000000000000..c0735a8098ccd --- /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 +#include + +void f1(std::span 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 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 0000000000000..f8aa512e9643f --- /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 +#include + +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 0000000000000..3d9333c7e219c --- /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 +#include + +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 +}