1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 // UNSUPPORTED: c++03, c++11, c++14, c++17
9
10 // Make sure that std::span's iterators check for OOB accesses when the debug mode is enabled.
11
12 // REQUIRES: has-unix-headers, libcpp-has-abi-bounded-iterators
13 // UNSUPPORTED: libcpp-hardening-mode=none
14
15 #include <span>
16
17 #include "check_assertion.h"
18
19 struct Foo {
20 int x;
21 };
22
23 template <typename Iter>
test_iterator(Iter begin,Iter end,bool reverse)24 void test_iterator(Iter begin, Iter end, bool reverse) {
25 std::ptrdiff_t distance = std::distance(begin, end);
26
27 // Dereferencing an iterator at the end.
28 {
29 TEST_LIBCPP_ASSERT_FAILURE(
30 *end,
31 reverse ? "__bounded_iter::operator--: Attempt to rewind an iterator past the start"
32 : "__bounded_iter::operator*: Attempt to dereference an iterator at the end");
33 #if _LIBCPP_STD_VER >= 20
34 // In C++20 mode, std::reverse_iterator implements operator->, but not operator*, with
35 // std::prev instead of operator--. std::prev ultimately calls operator+
36 TEST_LIBCPP_ASSERT_FAILURE(
37 end->x,
38 reverse ? "__bounded_iter::operator+=: Attempt to rewind an iterator past the start"
39 : "__bounded_iter::operator->: Attempt to dereference an iterator at the end");
40 #else
41 TEST_LIBCPP_ASSERT_FAILURE(
42 end->x,
43 reverse ? "__bounded_iter::operator--: Attempt to rewind an iterator past the start"
44 : "__bounded_iter::operator->: Attempt to dereference an iterator at the end");
45 #endif
46 }
47
48 // Incrementing an iterator past the end.
49 {
50 [[maybe_unused]] const char* msg =
51 reverse ? "__bounded_iter::operator--: Attempt to rewind an iterator past the start"
52 : "__bounded_iter::operator++: Attempt to advance an iterator past the end";
53 auto it = end;
54 TEST_LIBCPP_ASSERT_FAILURE(it++, msg);
55 TEST_LIBCPP_ASSERT_FAILURE(++it, msg);
56 }
57
58 // Decrementing an iterator past the start.
59 {
60 [[maybe_unused]] const char* msg =
61 reverse ? "__bounded_iter::operator++: Attempt to advance an iterator past the end"
62 : "__bounded_iter::operator--: Attempt to rewind an iterator past the start";
63 auto it = begin;
64 TEST_LIBCPP_ASSERT_FAILURE(it--, msg);
65 TEST_LIBCPP_ASSERT_FAILURE(--it, msg);
66 }
67
68 // Advancing past the end with operator+= and operator+.
69 {
70 [[maybe_unused]] const char* msg =
71 reverse ? "__bounded_iter::operator-=: Attempt to rewind an iterator past the start"
72 : "__bounded_iter::operator+=: Attempt to advance an iterator past the end";
73 auto it = end;
74 TEST_LIBCPP_ASSERT_FAILURE(it += 1, msg);
75 TEST_LIBCPP_ASSERT_FAILURE(end + 1, msg);
76 it = begin;
77 TEST_LIBCPP_ASSERT_FAILURE(it += (distance + 1), msg);
78 TEST_LIBCPP_ASSERT_FAILURE(begin + (distance + 1), msg);
79 }
80
81 // Advancing past the end with operator-= and operator-.
82 {
83 [[maybe_unused]] const char* msg =
84 reverse ? "__bounded_iter::operator+=: Attempt to rewind an iterator past the start"
85 : "__bounded_iter::operator-=: Attempt to advance an iterator past the end";
86 auto it = end;
87 TEST_LIBCPP_ASSERT_FAILURE(it -= (-1), msg);
88 TEST_LIBCPP_ASSERT_FAILURE(end - (-1), msg);
89 it = begin;
90 TEST_LIBCPP_ASSERT_FAILURE(it -= (-distance - 1), msg);
91 TEST_LIBCPP_ASSERT_FAILURE(begin - (-distance - 1), msg);
92 }
93
94 // Rewinding past the start with operator+= and operator+.
95 {
96 [[maybe_unused]] const char* msg =
97 reverse ? "__bounded_iter::operator-=: Attempt to advance an iterator past the end"
98 : "__bounded_iter::operator+=: Attempt to rewind an iterator past the start";
99 auto it = begin;
100 TEST_LIBCPP_ASSERT_FAILURE(it += (-1), msg);
101 TEST_LIBCPP_ASSERT_FAILURE(begin + (-1), msg);
102 it = end;
103 TEST_LIBCPP_ASSERT_FAILURE(it += (-distance - 1), msg);
104 TEST_LIBCPP_ASSERT_FAILURE(end + (-distance - 1), msg);
105 }
106
107 // Rewinding past the start with operator-= and operator-.
108 {
109 [[maybe_unused]] const char* msg =
110 reverse ? "__bounded_iter::operator+=: Attempt to advance an iterator past the end"
111 : "__bounded_iter::operator-=: Attempt to rewind an iterator past the start";
112 auto it = begin;
113 TEST_LIBCPP_ASSERT_FAILURE(it -= 1, msg);
114 TEST_LIBCPP_ASSERT_FAILURE(begin - 1, msg);
115 it = end;
116 TEST_LIBCPP_ASSERT_FAILURE(it -= (distance + 1), msg);
117 TEST_LIBCPP_ASSERT_FAILURE(end - (distance + 1), msg);
118 }
119
120 // Out-of-bounds operator[].
121 {
122 [[maybe_unused]] const char* end_msg =
123 reverse ? "__bounded_iter::operator--: Attempt to rewind an iterator past the start"
124 : "__bounded_iter::operator[]: Attempt to index an iterator at or past the end";
125 [[maybe_unused]] const char* past_end_msg =
126 reverse ? "__bounded_iter::operator-=: Attempt to rewind an iterator past the start"
127 : "__bounded_iter::operator[]: Attempt to index an iterator at or past the end";
128 [[maybe_unused]] const char* past_start_msg =
129 reverse ? "__bounded_iter::operator-=: Attempt to advance an iterator past the end"
130 : "__bounded_iter::operator[]: Attempt to index an iterator past the start";
131 TEST_LIBCPP_ASSERT_FAILURE(begin[distance], end_msg);
132 TEST_LIBCPP_ASSERT_FAILURE(begin[distance + 1], past_end_msg);
133 TEST_LIBCPP_ASSERT_FAILURE(begin[-1], past_start_msg);
134 TEST_LIBCPP_ASSERT_FAILURE(begin[-99], past_start_msg);
135
136 auto it = begin + 1;
137 TEST_LIBCPP_ASSERT_FAILURE(it[distance - 1], end_msg);
138 TEST_LIBCPP_ASSERT_FAILURE(it[distance], past_end_msg);
139 TEST_LIBCPP_ASSERT_FAILURE(it[-2], past_start_msg);
140 TEST_LIBCPP_ASSERT_FAILURE(it[-99], past_start_msg);
141 }
142 }
143
main(int,char **)144 int main(int, char**) {
145 // span<T>::iterator
146 {
147 Foo array[] = {{0}, {1}, {2}};
148 std::span<Foo> const span(array, 3);
149 test_iterator(span.begin(), span.end(), /*reverse=*/false);
150 }
151
152 // span<T, N>::iterator
153 {
154 Foo array[] = {{0}, {1}, {2}};
155 std::span<Foo, 3> const span(array, 3);
156 test_iterator(span.begin(), span.end(), /*reverse=*/false);
157 }
158
159 // span<T>::reverse_iterator
160 {
161 Foo array[] = {{0}, {1}, {2}};
162 std::span<Foo> const span(array, 3);
163 test_iterator(span.rbegin(), span.rend(), /*reverse=*/true);
164 }
165
166 // span<T, N>::reverse_iterator
167 {
168 Foo array[] = {{0}, {1}, {2}};
169 std::span<Foo, 3> const span(array, 3);
170 test_iterator(span.rbegin(), span.rend(), /*reverse=*/true);
171 }
172
173 return 0;
174 }
175