Skip to content

Commit bba4ded

Browse files
[libc++] Fix constructing bitset from non-null-terminated arrays (#143691)
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 Fixes #143684
1 parent ca5b71a commit bba4ded

File tree

2 files changed

+34
-8
lines changed

2 files changed

+34
-8
lines changed

libcxx/include/bitset

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -645,16 +645,13 @@ public:
645645
template <class _CharT, __enable_if_t<_IsCharLikeType<_CharT>::value, int> = 0>
646646
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 explicit bitset(
647647
const _CharT* __str,
648-
# if _LIBCPP_STD_VER >= 26
649-
typename basic_string_view<_CharT>::size_type __n = basic_string_view<_CharT>::npos,
650-
# else
651-
typename basic_string<_CharT>::size_type __n = basic_string<_CharT>::npos,
652-
# endif
648+
size_t __n = basic_string<_CharT>::npos,
653649
_CharT __zero = _CharT('0'),
654650
_CharT __one = _CharT('1')) {
655-
656-
size_t __rlen = std::min(__n, char_traits<_CharT>::length(__str));
657-
__init_from_string_view(basic_string_view<_CharT>(__str, __rlen), __zero, __one);
651+
if (__n == basic_string<_CharT>::npos)
652+
__init_from_string_view(basic_string_view<_CharT>(__str), __zero, __one);
653+
else
654+
__init_from_string_view(basic_string_view<_CharT>(__str, __n), __zero, __one);
658655
}
659656
# if _LIBCPP_STD_VER >= 26
660657
template <class _CharT, class _Traits>

libcxx/test/std/utilities/template.bitset/bitset.cons/char_ptr_ctor.pass.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,35 @@ TEST_CONSTEXPR_CXX23 void test_char_pointer_ctor()
7272
for (std::size_t i = 10; i < v.size(); ++i)
7373
assert(v[i] == false);
7474
}
75+
// Verify that this constructor doesn't read over the given bound.
76+
// See https://github.com/llvm/llvm-project/issues/143684
77+
{
78+
const char not_null_terminated[] = {'1', '0', '1', '0', '1', '0', '1', '0', '1', '0'};
79+
std::bitset<N> v(not_null_terminated, 10);
80+
std::size_t M = std::min<std::size_t>(v.size(), 10);
81+
for (std::size_t i = 0; i < M; ++i)
82+
assert(v[i] == (not_null_terminated[M - 1 - i] == '1'));
83+
for (std::size_t i = 10; i < v.size(); ++i)
84+
assert(!v[i]);
85+
}
86+
{
87+
const char not_null_terminated[] = {'1', 'a', '1', 'a', '1', 'a', '1', 'a', '1', 'a'};
88+
std::bitset<N> v(not_null_terminated, 10, 'a');
89+
std::size_t M = std::min<std::size_t>(v.size(), 10);
90+
for (std::size_t i = 0; i < M; ++i)
91+
assert(v[i] == (not_null_terminated[M - 1 - i] == '1'));
92+
for (std::size_t i = 10; i < v.size(); ++i)
93+
assert(!v[i]);
94+
}
95+
{
96+
const char not_null_terminated[] = {'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a'};
97+
std::bitset<N> v(not_null_terminated, 10, 'a', 'b');
98+
std::size_t M = std::min<std::size_t>(v.size(), 10);
99+
for (std::size_t i = 0; i < M; ++i)
100+
assert(v[i] == (not_null_terminated[M - 1 - i] == 'b'));
101+
for (std::size_t i = 10; i < v.size(); ++i)
102+
assert(!v[i]);
103+
}
75104
}
76105

77106
TEST_CONSTEXPR_CXX23 bool test() {

0 commit comments

Comments
 (0)