From 032ec4cc80f1c0f1baa0383bdbc5f9239b68f268 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Tue, 7 Jul 2026 13:40:04 +0200 Subject: [PATCH] Fix etl::bitset single-element from_string shift on empty string (#1486) The single-element from_string overloads (char, wchar_t, char16_t, char32_t) computed element_type(1) << (string_length - 1U) before the copy loop. For an empty string, or when active_bits is 0, string_length is 0, so the shift count underflows to SIZE_MAX. Shifting by more than the element width is undefined behaviour, which also breaks constexpr evaluation. Guard the mask with a zero-length check so the shift is only performed when string_length > 0. The multi-element from_string overloads were already safe. Add test_construct_from_empty_string regression test. Co-authored-by: John Wellbelove --- include/etl/private/bitset_new.h | 20 +++++++++++++++++++ ...itset_new_explicit_single_element_type.cpp | 16 +++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/include/etl/private/bitset_new.h b/include/etl/private/bitset_new.h index e33f3b10..ef1152cd 100644 --- a/include/etl/private/bitset_new.h +++ b/include/etl/private/bitset_new.h @@ -311,6 +311,11 @@ namespace etl // Build from the string. string_length = etl::min(active_bits, string_length); + if (string_length == 0U) + { + return; + } + element_type mask = element_type(element_type(1) << (string_length - 1U)); for (size_t i = 0U; i < string_length; ++i) @@ -339,6 +344,11 @@ namespace etl // Build from the string. string_length = etl::min(active_bits, string_length); + if (string_length == 0U) + { + return; + } + element_type mask = element_type(element_type(1) << (string_length - 1U)); for (size_t i = 0U; i < string_length; ++i) @@ -367,6 +377,11 @@ namespace etl // Build from the string. string_length = etl::min(active_bits, string_length); + if (string_length == 0U) + { + return; + } + element_type mask = element_type(element_type(1) << (string_length - 1U)); for (size_t i = 0U; i < string_length; ++i) @@ -395,6 +410,11 @@ namespace etl // Build from the string. string_length = etl::min(active_bits, string_length); + if (string_length == 0U) + { + return; + } + element_type mask = element_type(element_type(1) << (string_length - 1U)); for (size_t i = 0U; i < string_length; ++i) diff --git a/test/test_bitset_new_explicit_single_element_type.cpp b/test/test_bitset_new_explicit_single_element_type.cpp index de9bd2fe..3a44690b 100644 --- a/test/test_bitset_new_explicit_single_element_type.cpp +++ b/test/test_bitset_new_explicit_single_element_type.cpp @@ -398,6 +398,22 @@ namespace CHECK_EQUAL(0, data.count()); } + //************************************************************************* + // Empty strings exercise the string_length == 0 path in from_string, + // which must not shift by (0 - 1U). + TEST(test_construct_from_empty_string) + { + etl::bitset<64, uint64_t> data_char(""); + etl::bitset<64, uint64_t> data_wchar(L""); + etl::bitset<64, uint64_t> data_u16(u""); + etl::bitset<64, uint64_t> data_u32(U""); + + CHECK_EQUAL(0U, data_char.count()); + CHECK_EQUAL(0U, data_wchar.count()); + CHECK_EQUAL(0U, data_u16.count()); + CHECK_EQUAL(0U, data_u32.count()); + } + //************************************************************************* TEST(test_construct_from_excess_string) {