Skip to content

Commit bda7c9a

Browse files
authored
[libc++][hardening] Add checks to forward_list element access. (#120858)
In our implementation, failing these checks would result in a null pointer access rather than an out-of-bounds access.
1 parent 774c226 commit bda7c9a

File tree

3 files changed

+58
-3
lines changed

3 files changed

+58
-3
lines changed

libcxx/docs/Hardening.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ Hardened containers status
407407
- ✅
408408
- ❌
409409
* - ``forward_list``
410-
-
410+
-
411411
- ❌
412412
* - ``deque``
413413
- ✅

libcxx/include/forward_list

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ template <class T, class Allocator, class Predicate>
202202
# include <__algorithm/lexicographical_compare.h>
203203
# include <__algorithm/lexicographical_compare_three_way.h>
204204
# include <__algorithm/min.h>
205+
# include <__assert>
205206
# include <__config>
206207
# include <__cstddef/nullptr_t.h>
207208
# include <__iterator/distance.h>
@@ -766,8 +767,14 @@ public:
766767
return std::min<size_type>(__node_traits::max_size(this->__alloc_), numeric_limits<difference_type>::max());
767768
}
768769

769-
_LIBCPP_HIDE_FROM_ABI reference front() { return __base::__before_begin()->__next_->__get_value(); }
770-
_LIBCPP_HIDE_FROM_ABI const_reference front() const { return __base::__before_begin()->__next_->__get_value(); }
770+
_LIBCPP_HIDE_FROM_ABI reference front() {
771+
_LIBCPP_ASSERT_NON_NULL(!empty(), "forward_list::front called on an empty list");
772+
return __base::__before_begin()->__next_->__get_value();
773+
}
774+
_LIBCPP_HIDE_FROM_ABI const_reference front() const {
775+
_LIBCPP_ASSERT_NON_NULL(!empty(), "forward_list::front called on an empty list");
776+
return __base::__before_begin()->__next_->__get_value();
777+
}
771778

772779
# ifndef _LIBCPP_CXX03_LANG
773780
# if _LIBCPP_STD_VER >= 17
@@ -1085,6 +1092,7 @@ void forward_list<_Tp, _Alloc>::push_front(const value_type& __v) {
10851092

10861093
template <class _Tp, class _Alloc>
10871094
void forward_list<_Tp, _Alloc>::pop_front() {
1095+
_LIBCPP_ASSERT_NON_NULL(!empty(), "forward_list::pop_front called on an empty list");
10881096
__node_pointer __p = __base::__before_begin()->__next_;
10891097
__base::__before_begin()->__next_ = __p->__next_;
10901098
this->__delete_node(__p);
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
// <forward_list>
10+
11+
// Test hardening assertions for std::forward_list.
12+
13+
// REQUIRES: has-unix-headers
14+
// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
15+
// UNSUPPORTED: c++03
16+
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
17+
18+
#include <forward_list>
19+
20+
#include "check_assertion.h"
21+
22+
int main(int, char**) {
23+
{ // Default-constructed list.
24+
std::forward_list<int> c;
25+
const auto& const_c = c;
26+
TEST_LIBCPP_ASSERT_FAILURE(c.front(), "forward_list::front called on an empty list");
27+
TEST_LIBCPP_ASSERT_FAILURE(const_c.front(), "forward_list::front called on an empty list");
28+
TEST_LIBCPP_ASSERT_FAILURE(c.pop_front(), "forward_list::pop_front called on an empty list");
29+
}
30+
31+
{ // Non-empty list becomes empty.
32+
std::forward_list<int> c;
33+
const auto& const_c = c;
34+
c.push_front(1);
35+
36+
// Check that there's no assertion on valid access.
37+
(void)c.front();
38+
(void)const_c.front();
39+
40+
c.pop_front();
41+
TEST_LIBCPP_ASSERT_FAILURE(c.pop_front(), "forward_list::pop_front called on an empty list");
42+
TEST_LIBCPP_ASSERT_FAILURE(c.front(), "forward_list::front called on an empty list");
43+
TEST_LIBCPP_ASSERT_FAILURE(const_c.front(), "forward_list::front called on an empty list");
44+
}
45+
46+
return 0;
47+
}

0 commit comments

Comments
 (0)