[libcxx-commits] [libcxx] [libc++][hardening] Add a bounds check for `valarray` and `bitset`. (PR #120685)

Konstantin Varlamov via libcxx-commits libcxx-commits at lists.llvm.org
Sat Dec 21 13:54:39 PST 2024


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

>From 25114892fa1dd0c61f660536ba908223d46d01c7 Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Thu, 19 Dec 2024 22:28:26 -0800
Subject: [PATCH 1/3] [libc++][hardening] Add a bounds check for `valarray` and
 `bitset`.

Add a `valid-element-access` check to `valarray::operator[]` and
`bitset::operator[]`.
---
 libcxx/include/bitset                         |  8 +++-
 libcxx/include/valarray                       | 10 ++++-
 .../libcxx/numerics/numarray/assert.pass.cpp  | 42 +++++++++++++++++
 .../template.bitset/assert.abi-v1.pass.cpp    | 43 ++++++++++++++++++
 .../template.bitset/assert.abi-v2.pass.cpp    | 45 +++++++++++++++++++
 5 files changed, 145 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/test/libcxx/numerics/numarray/assert.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/template.bitset/assert.abi-v1.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/template.bitset/assert.abi-v2.pass.cpp

diff --git a/libcxx/include/bitset b/libcxx/include/bitset
index e2dc7b459f1c4a..b8c31d5ddd52fe 100644
--- a/libcxx/include/bitset
+++ b/libcxx/include/bitset
@@ -133,6 +133,7 @@ template <size_t N> struct hash<std::bitset<N>>;
 #  include <__algorithm/fill_n.h>
 #  include <__algorithm/find.h>
 #  include <__bit_reference>
+#  include <__assert>
 #  include <__config>
 #  include <__functional/hash.h>
 #  include <__functional/unary_function.h>
@@ -682,13 +683,18 @@ public:
 
   // element access:
 #  ifdef _LIBCPP_ABI_BITSET_VECTOR_BOOL_CONST_SUBSCRIPT_RETURN_BOOL
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool operator[](size_t __p) const { return __base::__make_ref(__p); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool operator[](size_t __p) const {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__p < _Size, "bitset::operator[] index out of bounds");
+    return __base::__make_ref(__p);
+  }
 #  else
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR const_reference operator[](size_t __p) const {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__p < _Size, "bitset::operator[] index out of bounds");
     return __base::__make_ref(__p);
   }
 #  endif
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 reference operator[](size_t __p) {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__p < _Size, "bitset::operator[] index out of bounds");
     return __base::__make_ref(__p);
   }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const;
diff --git a/libcxx/include/valarray b/libcxx/include/valarray
index af916096b5ef9d..e86367dac929df 100644
--- a/libcxx/include/valarray
+++ b/libcxx/include/valarray
@@ -821,9 +821,15 @@ public:
   _LIBCPP_HIDE_FROM_ABI valarray& operator=(const __val_expr<_ValExpr>& __v);
 
   // element access:
-  _LIBCPP_HIDE_FROM_ABI const value_type& operator[](size_t __i) const { return __begin_[__i]; }
+  _LIBCPP_HIDE_FROM_ABI const value_type& operator[](size_t __i) const {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < size(), "valarray::operator[] index out of bounds");
+    return __begin_[__i];
+  }
 
-  _LIBCPP_HIDE_FROM_ABI value_type& operator[](size_t __i) { return __begin_[__i]; }
+  _LIBCPP_HIDE_FROM_ABI value_type& operator[](size_t __i) {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < size(), "valarray::operator[] index out of bounds");
+    return __begin_[__i];
+  }
 
   // subset operations:
   _LIBCPP_HIDE_FROM_ABI __val_expr<__slice_expr<const valarray&> > operator[](slice __s) const;
diff --git a/libcxx/test/libcxx/numerics/numarray/assert.pass.cpp b/libcxx/test/libcxx/numerics/numarray/assert.pass.cpp
new file mode 100644
index 00000000000000..2bdf52340abfc8
--- /dev/null
+++ b/libcxx/test/libcxx/numerics/numarray/assert.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <valarray>
+
+// Test hardening assertions for std::valarray.
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: libcpp-hardening-mode=none
+// UNSUPPORTED: c++03
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <valarray>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  { // Empty valarray
+    std::valarray<int> c;
+    const auto& const_c = c;
+    TEST_LIBCPP_ASSERT_FAILURE(c[0], "valarray::operator[] index out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(const_c[0], "valarray::operator[] index out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(c[42], "valarray::operator[] index out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(const_c[42], "valarray::operator[] index out of bounds");
+  }
+
+  { // Non-empty valarray
+    std::valarray<int> c(4);
+    const auto& const_c = c;
+    (void)c[3]; // Check that there's no assertion on valid access.
+    TEST_LIBCPP_ASSERT_FAILURE(c[4], "valarray::operator[] index out of bounds");
+    (void)const_c[3]; // Check that there's no assertion on valid access.
+    TEST_LIBCPP_ASSERT_FAILURE(const_c[4], "valarray::operator[] index out of bounds");
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/template.bitset/assert.abi-v1.pass.cpp b/libcxx/test/libcxx/utilities/template.bitset/assert.abi-v1.pass.cpp
new file mode 100644
index 00000000000000..0abb94954ba554
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/template.bitset/assert.abi-v1.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <bitset>
+
+// Test hardening assertions for std::bitset using ABI v1 (where the const overload of `operator[]` returns
+// `const_reference` which is non-Standard behavior).
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: libcpp-hardening-mode=none
+// UNSUPPORTED: c++03
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <bitset>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  { // Empty bitset
+    std::bitset<0> c;
+    const auto& const_c = c;
+    TEST_LIBCPP_ASSERT_FAILURE(c[0], "bitset::operator[] index out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(const_c[0], "bitset::operator[] index out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(c[42], "bitset::operator[] index out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(const_c[42], "bitset::operator[] index out of bounds");
+  }
+
+  { // Non-empty bitset
+    std::bitset<4> c(42);
+    const auto& const_c = c;
+    (void)c[3]; // Check that there's no assertion on valid access.
+    TEST_LIBCPP_ASSERT_FAILURE(c[4], "bitset::operator[] index out of bounds");
+    (void)const_c[3]; // Check that there's no assertion on valid access.
+    TEST_LIBCPP_ASSERT_FAILURE(const_c[4], "bitset::operator[] index out of bounds");
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/template.bitset/assert.abi-v2.pass.cpp b/libcxx/test/libcxx/utilities/template.bitset/assert.abi-v2.pass.cpp
new file mode 100644
index 00000000000000..783980118207f7
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/template.bitset/assert.abi-v2.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
+//
+//===----------------------------------------------------------------------===//
+
+// <bitset>
+
+// Test hardening assertions for std::bitset using ABI >= v2 (where the const overload of `operator[]` returns `bool` as
+// mandated by the Standard).
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: libcpp-hardening-mode=none
+// UNSUPPORTED: c++03
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ABI_BITSET_VECTOR_BOOL_CONST_SUBSCRIPT_RETURN_BOOL=1
+
+#include <bitset>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  { // Empty bitset
+    std::bitset<0> c;
+    const auto& const_c = c;
+    TEST_LIBCPP_ASSERT_FAILURE(c[0], "bitset::operator[] index out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(const_c[0], "bitset::operator[] index out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(c[42], "bitset::operator[] index out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(const_c[42], "bitset::operator[] index out of bounds");
+  }
+
+  { // Non-empty bitset
+    std::bitset<4> c(42);
+    const auto& const_c = c;
+    (void)c[3]; // Check that there's no assertion on valid access.
+    TEST_LIBCPP_ASSERT_FAILURE(c[4], "bitset::operator[] index out of bounds");
+    (void)const_c[3]; // Check that there's no assertion on valid access.
+    TEST_LIBCPP_ASSERT_FAILURE(const_c[4], "bitset::operator[] index out of bounds");
+  }
+
+  return 0;
+}

>From 0f8bfd04b22a8eaf79ebdc07959cda25bd78e06e Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Thu, 19 Dec 2024 22:36:40 -0800
Subject: [PATCH 2/3] Fix include order

---
 libcxx/include/bitset | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/bitset b/libcxx/include/bitset
index b8c31d5ddd52fe..df9fdfe6dd9ef0 100644
--- a/libcxx/include/bitset
+++ b/libcxx/include/bitset
@@ -132,8 +132,8 @@ template <size_t N> struct hash<std::bitset<N>>;
 #  include <__algorithm/fill.h>
 #  include <__algorithm/fill_n.h>
 #  include <__algorithm/find.h>
-#  include <__bit_reference>
 #  include <__assert>
+#  include <__bit_reference>
 #  include <__config>
 #  include <__functional/hash.h>
 #  include <__functional/unary_function.h>

>From 61c48e57275fd036b17e5736aa0c3218bb1b3bbc Mon Sep 17 00:00:00 2001
From: Konstantin Varlamov <varconst at apple.com>
Date: Sat, 21 Dec 2024 13:54:14 -0800
Subject: [PATCH 3/3] Address feedback

---
 .../template.bitset/assert.abi-v2.pass.cpp    | 45 -------------------
 ...assert.abi-v1.pass.cpp => assert.pass.cpp} |  3 +-
 .../bitset.members/op_and_eq.pass.cpp         |  2 +
 3 files changed, 3 insertions(+), 47 deletions(-)
 delete mode 100644 libcxx/test/libcxx/utilities/template.bitset/assert.abi-v2.pass.cpp
 rename libcxx/test/libcxx/utilities/template.bitset/{assert.abi-v1.pass.cpp => assert.pass.cpp} (89%)

diff --git a/libcxx/test/libcxx/utilities/template.bitset/assert.abi-v2.pass.cpp b/libcxx/test/libcxx/utilities/template.bitset/assert.abi-v2.pass.cpp
deleted file mode 100644
index 783980118207f7..00000000000000
--- a/libcxx/test/libcxx/utilities/template.bitset/assert.abi-v2.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <bitset>
-
-// Test hardening assertions for std::bitset using ABI >= v2 (where the const overload of `operator[]` returns `bool` as
-// mandated by the Standard).
-
-// REQUIRES: has-unix-headers
-// UNSUPPORTED: libcpp-hardening-mode=none
-// UNSUPPORTED: c++03
-// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
-
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ABI_BITSET_VECTOR_BOOL_CONST_SUBSCRIPT_RETURN_BOOL=1
-
-#include <bitset>
-
-#include "check_assertion.h"
-
-int main(int, char**) {
-  { // Empty bitset
-    std::bitset<0> c;
-    const auto& const_c = c;
-    TEST_LIBCPP_ASSERT_FAILURE(c[0], "bitset::operator[] index out of bounds");
-    TEST_LIBCPP_ASSERT_FAILURE(const_c[0], "bitset::operator[] index out of bounds");
-    TEST_LIBCPP_ASSERT_FAILURE(c[42], "bitset::operator[] index out of bounds");
-    TEST_LIBCPP_ASSERT_FAILURE(const_c[42], "bitset::operator[] index out of bounds");
-  }
-
-  { // Non-empty bitset
-    std::bitset<4> c(42);
-    const auto& const_c = c;
-    (void)c[3]; // Check that there's no assertion on valid access.
-    TEST_LIBCPP_ASSERT_FAILURE(c[4], "bitset::operator[] index out of bounds");
-    (void)const_c[3]; // Check that there's no assertion on valid access.
-    TEST_LIBCPP_ASSERT_FAILURE(const_c[4], "bitset::operator[] index out of bounds");
-  }
-
-  return 0;
-}
diff --git a/libcxx/test/libcxx/utilities/template.bitset/assert.abi-v1.pass.cpp b/libcxx/test/libcxx/utilities/template.bitset/assert.pass.cpp
similarity index 89%
rename from libcxx/test/libcxx/utilities/template.bitset/assert.abi-v1.pass.cpp
rename to libcxx/test/libcxx/utilities/template.bitset/assert.pass.cpp
index 0abb94954ba554..4019bdf1318eb9 100644
--- a/libcxx/test/libcxx/utilities/template.bitset/assert.abi-v1.pass.cpp
+++ b/libcxx/test/libcxx/utilities/template.bitset/assert.pass.cpp
@@ -8,8 +8,7 @@
 
 // <bitset>
 
-// Test hardening assertions for std::bitset using ABI v1 (where the const overload of `operator[]` returns
-// `const_reference` which is non-Standard behavior).
+// Test hardening assertions for std::bitset.
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: libcpp-hardening-mode=none
diff --git a/libcxx/test/std/utilities/template.bitset/bitset.members/op_and_eq.pass.cpp b/libcxx/test/std/utilities/template.bitset/bitset.members/op_and_eq.pass.cpp
index e8ab264f4bab62..d87fd91b0356c3 100644
--- a/libcxx/test/std/utilities/template.bitset/bitset.members/op_and_eq.pass.cpp
+++ b/libcxx/test/std/utilities/template.bitset/bitset.members/op_and_eq.pass.cpp
@@ -6,6 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2000000
+
 // bitset<N>& operator&=(const bitset<N>& rhs); // constexpr since C++23
 
 #include <bitset>



More information about the libcxx-commits mailing list