Skip to content

Commit b054289

Browse files
authored
[libc++] Add missing hardening checks and tests for vector<bool> (#121366)
This PR adds VALID_ELEMENT_ACCESS and VALID_INPUT_RANGE checks for vector<bool>.
1 parent a3b4d91 commit b054289

File tree

2 files changed

+92
-6
lines changed

2 files changed

+92
-6
lines changed

libcxx/include/__vector/vector_bool.h

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -275,17 +275,33 @@ class _LIBCPP_TEMPLATE_VIS vector<bool, _Allocator> {
275275
}
276276
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_reverse_iterator crend() const _NOEXCEPT { return rend(); }
277277

278-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference operator[](size_type __n) { return __make_ref(__n); }
278+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference operator[](size_type __n) {
279+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n < size(), "vector<bool>::operator[] index out of bounds");
280+
return __make_ref(__n);
281+
}
279282
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_reference operator[](size_type __n) const {
283+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n < size(), "vector<bool>::operator[] index out of bounds");
280284
return __make_ref(__n);
281285
}
282286
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference at(size_type __n);
283287
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_reference at(size_type __n) const;
284288

285-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference front() { return __make_ref(0); }
286-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_reference front() const { return __make_ref(0); }
287-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference back() { return __make_ref(__size_ - 1); }
288-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_reference back() const { return __make_ref(__size_ - 1); }
289+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference front() {
290+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "vector<bool>::front() called on an empty vector");
291+
return __make_ref(0);
292+
}
293+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_reference front() const {
294+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "vector<bool>::front() called on an empty vector");
295+
return __make_ref(0);
296+
}
297+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference back() {
298+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "vector<bool>::back() called on an empty vector");
299+
return __make_ref(__size_ - 1);
300+
}
301+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_reference back() const {
302+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "vector<bool>::back() called on an empty vector");
303+
return __make_ref(__size_ - 1);
304+
}
289305

290306
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void push_back(const value_type& __x);
291307
#if _LIBCPP_STD_VER >= 14
@@ -310,7 +326,10 @@ class _LIBCPP_TEMPLATE_VIS vector<bool, _Allocator> {
310326
}
311327
#endif
312328

313-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void pop_back() { --__size_; }
329+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void pop_back() {
330+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "vector<bool>::pop_back called on an empty vector");
331+
--__size_;
332+
}
314333

315334
#if _LIBCPP_STD_VER >= 14
316335
template <class... _Args>
@@ -995,6 +1014,8 @@ vector<bool, _Allocator>::__insert_with_size(
9951014
template <class _Allocator>
9961015
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<bool, _Allocator>::iterator
9971016
vector<bool, _Allocator>::erase(const_iterator __position) {
1017+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
1018+
__position != end(), "vector<bool>::erase(iterator) called with a non-dereferenceable iterator");
9981019
iterator __r = __const_iterator_cast(__position);
9991020
std::copy(__position + 1, this->cend(), __r);
10001021
--__size_;
@@ -1004,6 +1025,8 @@ vector<bool, _Allocator>::erase(const_iterator __position) {
10041025
template <class _Allocator>
10051026
_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<bool, _Allocator>::iterator
10061027
vector<bool, _Allocator>::erase(const_iterator __first, const_iterator __last) {
1028+
_LIBCPP_ASSERT_VALID_INPUT_RANGE(
1029+
__first <= __last, "vector<bool>::erase(iterator, iterator) called with an invalid range");
10071030
iterator __r = __const_iterator_cast(__first);
10081031
difference_type __d = __last - __first;
10091032
std::copy(__last, this->cend(), __r);
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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+
// <vector>
10+
11+
// Test hardening assertions for std::vector<bool>.
12+
13+
// REQUIRES: has-unix-headers
14+
// UNSUPPORTED: libcpp-hardening-mode=none
15+
// UNSUPPORTED: c++03
16+
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
17+
18+
#include <vector>
19+
20+
#include "check_assertion.h"
21+
#include "min_allocator.h"
22+
23+
template <class Allocator>
24+
void test() {
25+
std::vector<bool, Allocator> c;
26+
TEST_LIBCPP_ASSERT_FAILURE(c.front(), "vector<bool>::front() called on an empty vector");
27+
TEST_LIBCPP_ASSERT_FAILURE(c.back(), "vector<bool>::back() called on an empty vector");
28+
TEST_LIBCPP_ASSERT_FAILURE(c[0], "vector<bool>::operator[] index out of bounds");
29+
TEST_LIBCPP_ASSERT_FAILURE(c.pop_back(), "vector<bool>::pop_back called on an empty vector");
30+
31+
// Repeat the test with a const reference to test the const overloads.
32+
{
33+
const std::vector<bool, Allocator>& cc = c;
34+
TEST_LIBCPP_ASSERT_FAILURE(cc.front(), "vector<bool>::front() called on an empty vector");
35+
TEST_LIBCPP_ASSERT_FAILURE(cc.back(), "vector<bool>::back() called on an empty vector");
36+
TEST_LIBCPP_ASSERT_FAILURE(cc[0], "vector<bool>::operator[] index out of bounds");
37+
}
38+
39+
c.push_back(true);
40+
c.push_back(false);
41+
c.push_back(true);
42+
TEST_LIBCPP_ASSERT_FAILURE(c[3], "vector<bool>::operator[] index out of bounds");
43+
TEST_LIBCPP_ASSERT_FAILURE(c[100], "vector<bool>::operator[] index out of bounds");
44+
45+
// Repeat the test with a const reference to test the const overloads.
46+
{
47+
const std::vector<bool, Allocator>& cc = c;
48+
TEST_LIBCPP_ASSERT_FAILURE(cc[3], "vector<bool>::operator[] index out of bounds");
49+
TEST_LIBCPP_ASSERT_FAILURE(cc[100], "vector<bool>::operator[] index out of bounds");
50+
}
51+
52+
TEST_LIBCPP_ASSERT_FAILURE(
53+
c.erase(c.end()), "vector<bool>::erase(iterator) called with a non-dereferenceable iterator");
54+
TEST_LIBCPP_ASSERT_FAILURE(
55+
c.erase(c.begin() + 1, c.begin()), "vector<bool>::erase(iterator, iterator) called with an invalid range");
56+
}
57+
58+
int main(int, char**) {
59+
test<std::allocator<bool>>();
60+
test<min_allocator<bool>>();
61+
62+
return 0;
63+
}

0 commit comments

Comments
 (0)