[libcxx-commits] [libcxx] [libc++][hardening] Add checks to `forward_list` element access. (PR #120858)

Konstantin Varlamov via libcxx-commits libcxx-commits at lists.llvm.org
Sat Dec 21 17:43:12 PST 2024


https://github.com/var-const updated https://github.com/llvm/llvm-project/pull/120858

>From efc849d93f8da1f09b50cd34de0ab91379ebcb0d Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Sat, 21 Dec 2024 14:52:11 -0800
Subject: [PATCH 1/3] [libc++][hardening] Add checks to `forward_list` element
 access.

In our implementation, failing these checks would result in a null
pointer access rather than an out-of-bounds access.
---
 libcxx/include/forward_list                   | 11 ++++-
 .../sequences/forwardlist/assert.pass.cpp     | 45 +++++++++++++++++++
 2 files changed, 54 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/containers/sequences/forwardlist/assert.pass.cpp

diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index c1ab155d5a133e..aa603da84f856e 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -766,8 +766,14 @@ public:
     return std::min<size_type>(__node_traits::max_size(this->__alloc_), numeric_limits<difference_type>::max());
   }
 
-  _LIBCPP_HIDE_FROM_ABI reference front() { return __base::__before_begin()->__next_->__get_value(); }
-  _LIBCPP_HIDE_FROM_ABI const_reference front() const { return __base::__before_begin()->__next_->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI reference front() {
+    _LIBCPP_ASSERT_NON_NULL(!empty(), "forward_list::front called on an empty list");
+    return __base::__before_begin()->__next_->__get_value();
+  }
+  _LIBCPP_HIDE_FROM_ABI const_reference front() const {
+    _LIBCPP_ASSERT_NON_NULL(!empty(), "forward_list::front called on an empty list");
+    return __base::__before_begin()->__next_->__get_value();
+  }
 
 #  ifndef _LIBCPP_CXX03_LANG
 #    if _LIBCPP_STD_VER >= 17
@@ -1085,6 +1091,7 @@ void forward_list<_Tp, _Alloc>::push_front(const value_type& __v) {
 
 template <class _Tp, class _Alloc>
 void forward_list<_Tp, _Alloc>::pop_front() {
+  _LIBCPP_ASSERT_NON_NULL(!empty(), "forward_list::pop_front called on an empty list");
   __node_pointer __p                = __base::__before_begin()->__next_;
   __base::__before_begin()->__next_ = __p->__next_;
   this->__delete_node(__p);
diff --git a/libcxx/test/libcxx/containers/sequences/forwardlist/assert.pass.cpp b/libcxx/test/libcxx/containers/sequences/forwardlist/assert.pass.cpp
new file mode 100644
index 00000000000000..e442d75a5904d6
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/forwardlist/assert.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <forward_list>
+
+// Test hardening assertions for std::forward_list.
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// UNSUPPORTED: c++03
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <forward_list>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  { // Default-constructed list.
+    std::forward_list<int> c;
+    const auto& const_c = c;
+    TEST_LIBCPP_ASSERT_FAILURE(c.front(), "forward_list::front called on an empty list");
+    TEST_LIBCPP_ASSERT_FAILURE(const_c.front(), "forward_list::front called on an empty list");
+    TEST_LIBCPP_ASSERT_FAILURE(c.pop_front(), "forward_list::pop_front called on an empty list");
+  }
+
+  { // Non-empty list becomes empty.
+    std::forward_list<int> c;
+    const auto& const_c = c;
+    c.push_front(1);
+
+    (void)c.front(); // Check that there's no assertion on valid access.
+    (void)const_c.front(); // Check that there's no assertion on valid access.
+    c.pop_front();
+    TEST_LIBCPP_ASSERT_FAILURE(c.pop_front(), "forward_list::pop_front called on an empty list");
+    TEST_LIBCPP_ASSERT_FAILURE(c.front(), "forward_list::front called on an empty list");
+    TEST_LIBCPP_ASSERT_FAILURE(const_c.front(), "forward_list::front called on an empty list");
+  }
+
+  return 0;
+}

>From a443582eb4837c4a8b233d7ea827512c91ab1183 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Sat, 21 Dec 2024 15:10:23 -0800
Subject: [PATCH 2/3] Formatting

---
 .../libcxx/containers/sequences/forwardlist/assert.pass.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/containers/sequences/forwardlist/assert.pass.cpp b/libcxx/test/libcxx/containers/sequences/forwardlist/assert.pass.cpp
index e442d75a5904d6..6d1748e6450256 100644
--- a/libcxx/test/libcxx/containers/sequences/forwardlist/assert.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/forwardlist/assert.pass.cpp
@@ -33,8 +33,10 @@ int main(int, char**) {
     const auto& const_c = c;
     c.push_front(1);
 
-    (void)c.front(); // Check that there's no assertion on valid access.
-    (void)const_c.front(); // Check that there's no assertion on valid access.
+    // Check that there's no assertion on valid access.
+    (void)c.front();
+    (void)const_c.front();
+
     c.pop_front();
     TEST_LIBCPP_ASSERT_FAILURE(c.pop_front(), "forward_list::pop_front called on an empty list");
     TEST_LIBCPP_ASSERT_FAILURE(c.front(), "forward_list::front called on an empty list");

>From 3360715ea55286dae0699d1556bd1664680b4c4a Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Sat, 21 Dec 2024 17:42:55 -0800
Subject: [PATCH 3/3] Fix CI

---
 libcxx/include/forward_list | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index aa603da84f856e..ea854ea828b3be 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -202,6 +202,7 @@ template <class T, class Allocator, class Predicate>
 #  include <__algorithm/lexicographical_compare.h>
 #  include <__algorithm/lexicographical_compare_three_way.h>
 #  include <__algorithm/min.h>
+#  include <__assert>
 #  include <__config>
 #  include <__cstddef/nullptr_t.h>
 #  include <__iterator/distance.h>



More information about the libcxx-commits mailing list