[libc-commits] [libc] [libc][printf] Fix %s precision handling (PR #233098)
via libc-commits
libc-commits at lists.llvm.org
Sat Oct 17 08:03:06 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: Nathan Cornaud (cornaud)
<details>
<summary>Changes</summary>
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.
A test is added for %s conversion with a specified precision, checking if it correctly formats a non-null-terminated input string when the input contains at least the required number of characters.
However, it is important to note that this test does not guarantee that the out-of-bounds bug will not reoccur, as detecting it would require running the test under ASan, and the bug itself may not be reproducible deterministically.
Fixes #<!-- -->230110 .
---
Full diff: https://github.com/llvm/llvm-project/pull/233098.diff
2 Files Affected:
- (modified) libc/src/__support/printf_core/string_converter.h (+7-5)
- (modified) libc/test/src/__support/printf_core/converter_test.cpp (+34)
``````````diff
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..3f2a96e82a535c 100644
--- a/libc/test/src/__support/printf_core/converter_test.cpp
+++ b/libc/test/src/__support/printf_core/converter_test.cpp
@@ -279,6 +279,40 @@ 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;
+ 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);
+
+ 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;
``````````
</details>
https://github.com/llvm/llvm-project/pull/233098
More information about the libc-commits
mailing list