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 | |
9 | // REQUIRES: has-unix-headers |
10 | // REQUIRES: libcpp-has-abi-bounded-iterators-in-std-array |
11 | // UNSUPPORTED: c++03 |
12 | // UNSUPPORTED: libcpp-hardening-mode=none |
13 | // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing |
14 | |
15 | // <array> |
16 | |
17 | // Make sure that std::array's iterators check for OOB accesses when the right hardening settings |
18 | // are enabled. |
19 | |
20 | #include <array> |
21 | #include <cstddef> |
22 | #include <iterator> |
23 | |
24 | #include "check_assertion.h" |
25 | |
26 | template <typename Iter> |
27 | void test_iterator(Iter begin, Iter end) { |
28 | std::ptrdiff_t distance = std::distance(begin, end); |
29 | |
30 | // Dereferencing an iterator at the end. |
31 | { |
32 | TEST_LIBCPP_ASSERT_FAILURE(*end, "__static_bounded_iter::operator*: Attempt to dereference an iterator at the end" ); |
33 | TEST_LIBCPP_ASSERT_FAILURE( |
34 | end.operator->(), "__static_bounded_iter::operator->: Attempt to dereference an iterator at the end" ); |
35 | } |
36 | |
37 | // Incrementing an iterator past the end. |
38 | { |
39 | auto it = end; |
40 | TEST_LIBCPP_ASSERT_FAILURE(it++, "__static_bounded_iter::operator++: Attempt to advance an iterator past the end" ); |
41 | it = end; |
42 | TEST_LIBCPP_ASSERT_FAILURE(++it, "__static_bounded_iter::operator++: Attempt to advance an iterator past the end" ); |
43 | } |
44 | |
45 | // Decrementing an iterator past the start. |
46 | { |
47 | auto it = begin; |
48 | TEST_LIBCPP_ASSERT_FAILURE(it--, "__static_bounded_iter::operator--: Attempt to rewind an iterator past the start" ); |
49 | it = begin; |
50 | TEST_LIBCPP_ASSERT_FAILURE(--it, "__static_bounded_iter::operator--: Attempt to rewind an iterator past the start" ); |
51 | } |
52 | |
53 | // Advancing past the end with operator+= and operator+. |
54 | { |
55 | [[maybe_unused]] const char* msg = "__static_bounded_iter::operator+=: Attempt to advance an iterator past the end" ; |
56 | auto it = end; |
57 | TEST_LIBCPP_ASSERT_FAILURE(it += 1, msg); |
58 | TEST_LIBCPP_ASSERT_FAILURE(end + 1, msg); |
59 | it = begin; |
60 | TEST_LIBCPP_ASSERT_FAILURE(it += (distance + 1), msg); |
61 | TEST_LIBCPP_ASSERT_FAILURE(begin + (distance + 1), msg); |
62 | } |
63 | |
64 | // Advancing past the end with operator-= and operator-. |
65 | { |
66 | [[maybe_unused]] const char* msg = "__static_bounded_iter::operator-=: Attempt to advance an iterator past the end" ; |
67 | auto it = end; |
68 | TEST_LIBCPP_ASSERT_FAILURE(it -= (-1), msg); |
69 | TEST_LIBCPP_ASSERT_FAILURE(end - (-1), msg); |
70 | it = begin; |
71 | TEST_LIBCPP_ASSERT_FAILURE(it -= (-distance - 1), msg); |
72 | TEST_LIBCPP_ASSERT_FAILURE(begin - (-distance - 1), msg); |
73 | } |
74 | |
75 | // Rewinding past the start with operator+= and operator+. |
76 | { |
77 | [[maybe_unused]] const char* msg = |
78 | "__static_bounded_iter::operator+=: Attempt to rewind an iterator past the start" ; |
79 | auto it = begin; |
80 | TEST_LIBCPP_ASSERT_FAILURE(it += (-1), msg); |
81 | TEST_LIBCPP_ASSERT_FAILURE(begin + (-1), msg); |
82 | it = end; |
83 | TEST_LIBCPP_ASSERT_FAILURE(it += (-distance - 1), msg); |
84 | TEST_LIBCPP_ASSERT_FAILURE(end + (-distance - 1), msg); |
85 | } |
86 | |
87 | // Rewinding past the start with operator-= and operator-. |
88 | { |
89 | [[maybe_unused]] const char* msg = |
90 | "__static_bounded_iter::operator-=: Attempt to rewind an iterator past the start" ; |
91 | auto it = begin; |
92 | TEST_LIBCPP_ASSERT_FAILURE(it -= 1, msg); |
93 | TEST_LIBCPP_ASSERT_FAILURE(begin - 1, msg); |
94 | it = end; |
95 | TEST_LIBCPP_ASSERT_FAILURE(it -= (distance + 1), msg); |
96 | TEST_LIBCPP_ASSERT_FAILURE(end - (distance + 1), msg); |
97 | } |
98 | |
99 | // Out-of-bounds operator[]. |
100 | { |
101 | [[maybe_unused]] const char* end_msg = |
102 | "__static_bounded_iter::operator[]: Attempt to index an iterator at or past the end" ; |
103 | [[maybe_unused]] const char* past_end_msg = |
104 | "__static_bounded_iter::operator[]: Attempt to index an iterator at or past the end" ; |
105 | [[maybe_unused]] const char* past_start_msg = |
106 | "__static_bounded_iter::operator[]: Attempt to index an iterator past the start" ; |
107 | TEST_LIBCPP_ASSERT_FAILURE(begin[distance], end_msg); |
108 | TEST_LIBCPP_ASSERT_FAILURE(begin[distance + 1], past_end_msg); |
109 | TEST_LIBCPP_ASSERT_FAILURE(begin[-1], past_start_msg); |
110 | TEST_LIBCPP_ASSERT_FAILURE(begin[-99], past_start_msg); |
111 | |
112 | if (distance > 0) { |
113 | auto it = begin + 1; |
114 | TEST_LIBCPP_ASSERT_FAILURE(it[distance - 1], end_msg); |
115 | TEST_LIBCPP_ASSERT_FAILURE(it[distance], past_end_msg); |
116 | TEST_LIBCPP_ASSERT_FAILURE(it[-2], past_start_msg); |
117 | TEST_LIBCPP_ASSERT_FAILURE(it[-99], past_start_msg); |
118 | } |
119 | } |
120 | } |
121 | |
122 | int main(int, char**) { |
123 | // Empty array |
124 | { |
125 | std::array<int, 0> array = {}; |
126 | |
127 | // array::iterator |
128 | test_iterator(begin: array.begin(), end: array.end()); |
129 | |
130 | // array::const_iterator |
131 | test_iterator(begin: array.cbegin(), end: array.cend()); |
132 | } |
133 | |
134 | // Non-empty array |
135 | { |
136 | std::array<int, 10> array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; |
137 | |
138 | // array::iterator |
139 | test_iterator(begin: array.begin(), end: array.end()); |
140 | |
141 | // array::const_iterator |
142 | test_iterator(begin: array.cbegin(), end: array.cend()); |
143 | } |
144 | |
145 | return 0; |
146 | } |
147 | |