diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -41,6 +41,9 @@ Improvements and New Features ----------------------------- +- ``std::string_view`` now provides iterators that check for out-of-bounds accesses when the safe + libc++ mode is enabled. + Deprecations and Removals ------------------------- diff --git a/libcxx/include/string_view b/libcxx/include/string_view --- a/libcxx/include/string_view +++ b/libcxx/include/string_view @@ -208,6 +208,7 @@ #include <__functional/hash.h> #include <__functional/unary_function.h> #include <__fwd/string_view.h> +#include <__iterator/bounded_iter.h> #include <__iterator/concepts.h> #include <__iterator/readable_traits.h> #include <__iterator/reverse_iterator.h> @@ -265,7 +266,11 @@ using const_pointer = const _CharT*; using reference = _CharT&; using const_reference = const _CharT&; +#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING + using const_iterator = __bounded_iter; +#else using const_iterator = const_pointer; // See [string.view.iterators] +#endif using iterator = const_iterator; using const_reverse_iterator = _VSTD::reverse_iterator; using reverse_iterator = const_reverse_iterator; @@ -343,10 +348,22 @@ const_iterator end() const _NOEXCEPT { return cend(); } _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY - const_iterator cbegin() const _NOEXCEPT { return __data_; } + const_iterator cbegin() const _NOEXCEPT { +#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING + return std::__make_bounded_iter(data(), data(), data() + size()); +#else + return __data_; +#endif + } _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY - const_iterator cend() const _NOEXCEPT { return __data_ + __size_; } + const_iterator cend() const _NOEXCEPT { +#ifdef _LIBCPP_DEBUG_ITERATOR_BOUNDS_CHECKING + return std::__make_bounded_iter(data() + size(), data(), data() + size()); +#else + return __data_ + __size_; +#endif + } _LIBCPP_CONSTEXPR_SINCE_CXX17 _LIBCPP_INLINE_VISIBILITY const_reverse_iterator rbegin() const _NOEXCEPT { return const_reverse_iterator(cend()); } diff --git a/libcxx/test/std/strings/string.view/string.view.iterators/debug.iterator-indexing.pass.cpp b/libcxx/test/std/strings/string.view/string.view.iterators/debug.iterator-indexing.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/string.view/string.view.iterators/debug.iterator-indexing.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Make sure that std::string_view's iterators check for OOB accesses when the debug mode is enabled. + +// REQUIRES: has-unix-headers +// UNSUPPORTED: !libcpp-has-debug-mode + +#include + +#include "check_assertion.h" + +struct Foo { + int x; +}; + +int main(int, char**) { + // string_view::iterator + { + std::string_view const str("hello world"); + { + auto it = str.end(); + TEST_LIBCPP_ASSERT_FAILURE(*it, "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator"); + } + { + auto it = str.end(); + TEST_LIBCPP_ASSERT_FAILURE(it.operator->(), "__bounded_iter::operator->: Attempt to dereference an out-of-range iterator"); + } + { + auto it = str.begin(); + TEST_LIBCPP_ASSERT_FAILURE(it[99], "__bounded_iter::operator[]: Attempt to index an iterator out-of-range"); + } + } + + // string_view::reverse_iterator + { + std::string_view const str("hello world"); + { + auto it = str.rend(); + TEST_LIBCPP_ASSERT_FAILURE(*it, "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator"); + } + { + auto it = str.rend(); + TEST_LIBCPP_ASSERT_FAILURE(it.operator->(), "__bounded_iter::operator->: Attempt to dereference an out-of-range iterator"); + } + { + auto it = str.rbegin(); + TEST_LIBCPP_ASSERT_FAILURE(it[99], "__bounded_iter::operator*: Attempt to dereference an out-of-range iterator"); + } + } + + return 0; +}