[libcxx-commits] [libcxx] [libc++] Fix constructing `bitset` from non-null-terminated arrays (PR #143691)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jun 11 05:56:42 PDT 2025


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/143691

>From 930f9566240747584cba17b9102d6317be72a979 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 11 Jun 2025 20:18:45 +0800
Subject: [PATCH 1/3] [libc++] Fix constructing `bitset` from
 non-null-terminated arrays

Unconditional evaluation of `char_traits<_CharT>::length(__str)` is
problematic, because it causes UB when `__str` points to a
non-null-terminated array. We should only call `length` (currently, in
`basic_string_view`'s constructor) when `__n == npos` per
[bitset.cons]/8.

Drive-by change: Reduction of conditional compilation, given that
- both `basic_string_view<_CharT>::size_type` and
`basic_string<_CharT>::size_type` must be `size_t`, and thus
- both `basic_string_view<_CharT>::npos` and
`basic_string<_CharT>::npos` must be `size_t(-1)`.

For the type sameness in the standard wording, see:
- [string.view.template.general]
- [basic.string.general]
- [allocator.traits.types]/6
- [default.allocator.general]/1
---
 libcxx/include/bitset                         | 13 ++++-----
 .../bitset.cons/char_ptr_ctor.pass.cpp        | 28 +++++++++++++++++++
 2 files changed, 33 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/bitset b/libcxx/include/bitset
index 88dc0e08c995d..8f2c0611b6436 100644
--- a/libcxx/include/bitset
+++ b/libcxx/include/bitset
@@ -645,16 +645,13 @@ public:
   template <class _CharT, __enable_if_t<_IsCharLikeType<_CharT>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 explicit bitset(
       const _CharT* __str,
-#  if _LIBCPP_STD_VER >= 26
-      typename basic_string_view<_CharT>::size_type __n = basic_string_view<_CharT>::npos,
-#  else
-      typename basic_string<_CharT>::size_type __n = basic_string<_CharT>::npos,
-#  endif
+      size_t __n    = static_cast<size_t>(-1),
       _CharT __zero = _CharT('0'),
       _CharT __one  = _CharT('1')) {
-
-    size_t __rlen = std::min(__n, char_traits<_CharT>::length(__str));
-    __init_from_string_view(basic_string_view<_CharT>(__str, __rlen), __zero, __one);
+    if (__n == static_cast<size_t>(-1))
+      __init_from_string_view(basic_string_view<_CharT>(__str), __zero, __one);
+    else
+      __init_from_string_view(basic_string_view<_CharT>(__str, __n), __zero, __one);
   }
 #  if _LIBCPP_STD_VER >= 26
   template <class _CharT, class _Traits>
diff --git a/libcxx/test/std/utilities/template.bitset/bitset.cons/char_ptr_ctor.pass.cpp b/libcxx/test/std/utilities/template.bitset/bitset.cons/char_ptr_ctor.pass.cpp
index 86b144ed87b70..8ad7d513ea7c5 100644
--- a/libcxx/test/std/utilities/template.bitset/bitset.cons/char_ptr_ctor.pass.cpp
+++ b/libcxx/test/std/utilities/template.bitset/bitset.cons/char_ptr_ctor.pass.cpp
@@ -72,6 +72,34 @@ TEST_CONSTEXPR_CXX23 void test_char_pointer_ctor()
     for (std::size_t i = 10; i < v.size(); ++i)
         assert(v[i] == false);
   }
+  // See https://github.com/llvm/llvm-project/issues/143684
+  {
+    const char not_null_terminated[] = {'1', '0', '1', '0', '1', '0', '1', '0', '1', '0'};
+    std::bitset<N> v(not_null_terminated, 10);
+    std::size_t M = std::min<std::size_t>(v.size(), 10);
+    for (std::size_t i = 0; i < M; ++i)
+      assert(v[i] == (not_null_terminated[M - 1 - i] == '1'));
+    for (std::size_t i = 10; i < v.size(); ++i)
+      assert(v[i] == false);
+  }
+  {
+    const char not_null_terminated[] = {'1', 'a', '1', 'a', '1', 'a', '1', 'a', '1', 'a'};
+    std::bitset<N> v(not_null_terminated, 10, 'a');
+    std::size_t M = std::min<std::size_t>(v.size(), 10);
+    for (std::size_t i = 0; i < M; ++i)
+      assert(v[i] == (not_null_terminated[M - 1 - i] == '1'));
+    for (std::size_t i = 10; i < v.size(); ++i)
+      assert(v[i] == false);
+  }
+  {
+    const char not_null_terminated[] = {'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a'};
+    std::bitset<N> v(not_null_terminated, 10, 'a', 'b');
+    std::size_t M = std::min<std::size_t>(v.size(), 10);
+    for (std::size_t i = 0; i < M; ++i)
+      assert(v[i] == (not_null_terminated[M - 1 - i] == 'b'));
+    for (std::size_t i = 10; i < v.size(); ++i)
+      assert(v[i] == false);
+  }
 }
 
 TEST_CONSTEXPR_CXX23 bool test() {

>From 5a9c867261eaf99a30bc942f5ea228123d71757c Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 11 Jun 2025 20:39:10 +0800
Subject: [PATCH 2/3] Restore `basic_string<_CharT>::npos` and add a proper
 description.

---
 libcxx/include/bitset                                         | 4 ++--
 .../template.bitset/bitset.cons/char_ptr_ctor.pass.cpp        | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/bitset b/libcxx/include/bitset
index 8f2c0611b6436..6be476e2b69d8 100644
--- a/libcxx/include/bitset
+++ b/libcxx/include/bitset
@@ -645,10 +645,10 @@ public:
   template <class _CharT, __enable_if_t<_IsCharLikeType<_CharT>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 explicit bitset(
       const _CharT* __str,
-      size_t __n    = static_cast<size_t>(-1),
+      size_t __n    = basic_string<_CharT>::npos,
       _CharT __zero = _CharT('0'),
       _CharT __one  = _CharT('1')) {
-    if (__n == static_cast<size_t>(-1))
+    if (__n == basic_string<_CharT>::npos)
       __init_from_string_view(basic_string_view<_CharT>(__str), __zero, __one);
     else
       __init_from_string_view(basic_string_view<_CharT>(__str, __n), __zero, __one);
diff --git a/libcxx/test/std/utilities/template.bitset/bitset.cons/char_ptr_ctor.pass.cpp b/libcxx/test/std/utilities/template.bitset/bitset.cons/char_ptr_ctor.pass.cpp
index 8ad7d513ea7c5..97637c5d1c85e 100644
--- a/libcxx/test/std/utilities/template.bitset/bitset.cons/char_ptr_ctor.pass.cpp
+++ b/libcxx/test/std/utilities/template.bitset/bitset.cons/char_ptr_ctor.pass.cpp
@@ -72,6 +72,7 @@ TEST_CONSTEXPR_CXX23 void test_char_pointer_ctor()
     for (std::size_t i = 10; i < v.size(); ++i)
         assert(v[i] == false);
   }
+  // Verify that this constructor doesn't read over the given bound.
   // See https://github.com/llvm/llvm-project/issues/143684
   {
     const char not_null_terminated[] = {'1', '0', '1', '0', '1', '0', '1', '0', '1', '0'};

>From 1ec0c23ead692f77bcdabd19865f55916093f7d8 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 11 Jun 2025 20:56:21 +0800
Subject: [PATCH 3/3] Change `v[i] == false` to `!v[i]` as it's type is
 boolean-testable

---
 .../template.bitset/bitset.cons/char_ptr_ctor.pass.cpp      | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/utilities/template.bitset/bitset.cons/char_ptr_ctor.pass.cpp b/libcxx/test/std/utilities/template.bitset/bitset.cons/char_ptr_ctor.pass.cpp
index 97637c5d1c85e..4f9cdaeb38c0b 100644
--- a/libcxx/test/std/utilities/template.bitset/bitset.cons/char_ptr_ctor.pass.cpp
+++ b/libcxx/test/std/utilities/template.bitset/bitset.cons/char_ptr_ctor.pass.cpp
@@ -81,7 +81,7 @@ TEST_CONSTEXPR_CXX23 void test_char_pointer_ctor()
     for (std::size_t i = 0; i < M; ++i)
       assert(v[i] == (not_null_terminated[M - 1 - i] == '1'));
     for (std::size_t i = 10; i < v.size(); ++i)
-      assert(v[i] == false);
+      assert(!v[i]);
   }
   {
     const char not_null_terminated[] = {'1', 'a', '1', 'a', '1', 'a', '1', 'a', '1', 'a'};
@@ -90,7 +90,7 @@ TEST_CONSTEXPR_CXX23 void test_char_pointer_ctor()
     for (std::size_t i = 0; i < M; ++i)
       assert(v[i] == (not_null_terminated[M - 1 - i] == '1'));
     for (std::size_t i = 10; i < v.size(); ++i)
-      assert(v[i] == false);
+      assert(!v[i]);
   }
   {
     const char not_null_terminated[] = {'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a'};
@@ -99,7 +99,7 @@ TEST_CONSTEXPR_CXX23 void test_char_pointer_ctor()
     for (std::size_t i = 0; i < M; ++i)
       assert(v[i] == (not_null_terminated[M - 1 - i] == 'b'));
     for (std::size_t i = 10; i < v.size(); ++i)
-      assert(v[i] == false);
+      assert(!v[i]);
   }
 }
 



More information about the libcxx-commits mailing list