[libc-commits] [libc] [libc][printf] Fix %s precision handling (PR #233098)
Nathan Cornaud via libc-commits
libc-commits at lists.llvm.org
Sun Oct 18 03:22:58 PDT 2026
https://github.com/cornaud updated https://github.com/llvm/llvm-project/pull/233098
>From 7a2474756da3929ef948d2d0d69095967aa0d7f1 Mon Sep 17 00:00:00 2001
From: Nathan-C <ncornaud at gmail.com>
Date: Sat, 17 Oct 2026 00:42:11 +0200
Subject: [PATCH 1/2] [libc][printf] Fix %s precision handling
This fixes an issue where the string converter for `%s` attempts to retrieve the string length of the argument by climbing to the null-terminator regardless of precision, even tho if a precision is specified the null-terminator isn't mandatory.
Now, the string length is calculated up to the null-terminator only if no precision is specified, otherwise the length calculation is capped up to the precision.
Fixes #230110.
---
.../__support/printf_core/string_converter.h | 12 ++++----
.../__support/printf_core/converter_test.cpp | 30 +++++++++++++++++++
2 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/libc/src/__support/printf_core/string_converter.h b/libc/src/__support/printf_core/string_converter.h
index 4a72ee03d43ada..1f82ff79893694 100644
--- a/libc/src/__support/printf_core/string_converter.h
+++ b/libc/src/__support/printf_core/string_converter.h
@@ -40,11 +40,13 @@ LIBC_INLINE int write_char_string_as_chars(Writer<mode, char> *writer,
}
#endif // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
- string_len = internal::string_length(str_ptr);
-
- if (to_conv.precision >= 0 &&
- static_cast<size_t>(to_conv.precision) < string_len)
- string_len = to_conv.precision;
+ // If a precision is specified, it doesn't have to be null-terminated.
+ if (to_conv.precision >= 0) {
+ while (string_len < static_cast<size_t>(to_conv.precision) &&
+ str_ptr[string_len] != '\0')
+ string_len++;
+ } else
+ string_len = internal::string_length(str_ptr);
size_t padding_spaces = to_conv.min_width > static_cast<int>(string_len)
? to_conv.min_width - string_len
diff --git a/libc/test/src/__support/printf_core/converter_test.cpp b/libc/test/src/__support/printf_core/converter_test.cpp
index facbda405acb7d..17bd63c3817f60 100644
--- a/libc/test/src/__support/printf_core/converter_test.cpp
+++ b/libc/test/src/__support/printf_core/converter_test.cpp
@@ -279,6 +279,36 @@ TYPED_TEST(LlvmLibcPrintfConverterTest, StringConversionPrecisionLow,
ASSERT_EQ(writer.get_chars_written(), size_t{2});
}
+TYPED_TEST(LlvmLibcPrintfConverterTest, StringConversionNoNullTerminator,
+ TestCharTypes) {
+ // When a precision is specified for a %s conversion, the input string does
+ // not need to be null-terminated, provided it contains at least as many
+ // characters as the specified precision.
+
+ using CharT = ParamType;
+ CharT str[60];
+ Writer writer =
+ make_drop_overflow_writer(str, sizeof(str) / sizeof(CharT) - 1);
+ WriteBuffer<CharT> &wb = writer.get_write_buffer();
+
+ const CharT input[] = {ENCODED(CharT, 'j'), ENCODED(CharT, 'k'),
+ ENCODED(CharT, 'l')};
+
+ FormatSection<CharT> no_null_terminator_conv;
+ no_null_terminator_conv.has_conv = true;
+ no_null_terminator_conv.raw_string = ENCODED(CharT, "%.3s");
+ no_null_terminator_conv.conv_name = ENCODED(CharT, 's');
+ no_null_terminator_conv.precision = 3;
+ no_null_terminator_conv.conv_val_ptr = const_cast<CharT *>(input);
+
+ LIBC_NAMESPACE::printf_core::convert(&writer, no_null_terminator_conv);
+
+ wb.buff[wb.buff_cur] = ENCODED(CharT, '\0');
+
+ ASSERT_STREQ(str, ENCODED(CharT, "jkl"));
+ ASSERT_EQ(writer.get_chars_written(), size_t{3});
+}
+
TYPED_TEST(LlvmLibcPrintfConverterTest, StringConversionRightJustified,
TestCharTypes) {
using CharT = ParamType;
>From c9b9193ce9df353be33ca989f758edd4d4ea321f Mon Sep 17 00:00:00 2001
From: cornaud <ncornaud at gmail.com>
Date: Sat, 17 Oct 2026 16:31:49 +0200
Subject: [PATCH 2/2] [libc] Fix wide string conversion test without null
terminator
Set the l length modifier for the wchar_t case in StringConversionNoNullTerminator so the converter interprets the input as a wide string and correctly verifies the expected output.
This fixes 7a24747 (#230110).
---
libc/test/src/__support/printf_core/converter_test.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/libc/test/src/__support/printf_core/converter_test.cpp b/libc/test/src/__support/printf_core/converter_test.cpp
index 17bd63c3817f60..427a7d1fedad2f 100644
--- a/libc/test/src/__support/printf_core/converter_test.cpp
+++ b/libc/test/src/__support/printf_core/converter_test.cpp
@@ -299,6 +299,10 @@ TYPED_TEST(LlvmLibcPrintfConverterTest, StringConversionNoNullTerminator,
no_null_terminator_conv.raw_string = ENCODED(CharT, "%.3s");
no_null_terminator_conv.conv_name = ENCODED(CharT, 's');
no_null_terminator_conv.precision = 3;
+ if constexpr (LIBC_NAMESPACE::cpp::is_same_v<CharT, wchar_t>) {
+ no_null_terminator_conv.length_modifier =
+ LIBC_NAMESPACE::printf_core::LengthModifier::l;
+ }
no_null_terminator_conv.conv_val_ptr = const_cast<CharT *>(input);
LIBC_NAMESPACE::printf_core::convert(&writer, no_null_terminator_conv);
More information about the libc-commits
mailing list