From 4ccf1d4faf977a77f157fcad2b96b663109f59cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Fri, 10 Apr 2026 21:23:57 +0200 Subject: [PATCH] Fix out-of-bounds read in vprintf with trailing '%' (#4742) --- include/fmt/printf.h | 2 ++ test/printf-test.cc | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/fmt/printf.h b/include/fmt/printf.h index c2e92291..235dbb06 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -438,6 +438,8 @@ void vprintf(buffer& buf, basic_string_view format, } write(out, basic_string_view(start, to_unsigned(it - 1 - start))); + if (it == end) report_error("invalid format string"); + auto specs = format_specs(); specs.set_align(align::right); diff --git a/test/printf-test.cc b/test/printf-test.cc index 354143b9..b1478526 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -46,6 +46,14 @@ auto test_sprintf(fmt::basic_string_view format, const Args&... args) TEST(printf_test, no_args) { EXPECT_EQ("test", test_sprintf("test")); } +TEST(printf_test, trailing_percent) { + EXPECT_THROW_MSG(test_sprintf("%"), format_error, "invalid format string"); + EXPECT_THROW_MSG(test_sprintf("hello%"), format_error, + "invalid format string"); + EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2), format_error, + "invalid format string"); +} + TEST(printf_test, escape) { EXPECT_EQ("%", test_sprintf("%%")); EXPECT_EQ("before %", test_sprintf("before %%")); @@ -76,8 +84,6 @@ TEST(printf_test, number_is_too_big_in_arg_index) { } TEST(printf_test, switch_arg_indexing) { - EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2), format_error, - "cannot switch from manual to automatic argument indexing"); EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", big_num), 1, 2), format_error, "number is too big"); EXPECT_THROW_MSG(test_sprintf("%1$d%d", 1, 2), format_error,