Don't assume nul termination in printf

Thanks ZUENS2020 for reporting.
This commit is contained in:
Victor Zverovich 2026-03-23 09:22:38 -07:00
parent ea85b81ccd
commit dc05bee307
4 changed files with 13 additions and 19 deletions

View File

@ -48,6 +48,8 @@ jobs:
- cxx: clang++-14 - cxx: clang++-14
build_type: Debug build_type: Debug
std: 20 std: 20
cxxflags: -fsanitize=address
cxxflags_extra: -fno-sanitize-recover=all -fno-omit-frame-pointer
- cxx: clang++-14 - cxx: clang++-14
build_type: Debug build_type: Debug
std: 20 std: 20
@ -154,7 +156,7 @@ jobs:
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
env: env:
CXX: ${{matrix.cxx}} CXX: ${{matrix.cxx}}
CXXFLAGS: ${{matrix.cxxflags}} CXXFLAGS: ${{matrix.cxxflags}} ${{matrix.cxxflags_extra}}
run: | run: |
cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DCMAKE_CXX_STANDARD=${{matrix.std}} \ -DCMAKE_CXX_STANDARD=${{matrix.std}} \

View File

@ -330,6 +330,7 @@ template <typename Char, typename GetArg>
auto parse_header(const Char*& it, const Char* end, format_specs& specs, auto parse_header(const Char*& it, const Char* end, format_specs& specs,
GetArg get_arg) -> int { GetArg get_arg) -> int {
int arg_index = -1; int arg_index = -1;
if (it == end) return arg_index;
Char c = *it; Char c = *it;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly // Parse an argument index (if followed by '$') or a width possibly

View File

@ -268,22 +268,6 @@ TEST(util_test, format_system_error) {
fmt::format_system_error(message, EDOM, "test"); fmt::format_system_error(message, EDOM, "test");
auto ec = std::error_code(EDOM, std::generic_category()); auto ec = std::error_code(EDOM, std::generic_category());
EXPECT_EQ(to_string(message), std::system_error(ec, "test").what()); EXPECT_EQ(to_string(message), std::system_error(ec, "test").what());
message = fmt::memory_buffer();
// Check if std::allocator throws on allocating max size_t / 2 chars.
size_t max_size = max_value<size_t>() / 2;
bool throws_on_alloc = false;
try {
auto alloc = std::allocator<char>();
alloc.deallocate(alloc.allocate(max_size), max_size);
} catch (const std::bad_alloc&) {
throws_on_alloc = true;
}
if (!throws_on_alloc) {
fmt::print(stderr, "warning: std::allocator allocates {} chars\n",
max_size);
return;
}
} }
TEST(util_test, system_error) { TEST(util_test, system_error) {

View File

@ -313,7 +313,8 @@ TEST(printf_test, positional_precision) {
EXPECT_EQ("Hell", test_sprintf("%2$.*1$s", 4, "Hello")); EXPECT_EQ("Hell", test_sprintf("%2$.*1$s", 4, "Hello"));
EXPECT_THROW_MSG(test_sprintf("%2$.*1$d", 5.0, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%2$.*1$d", 5.0, 42), format_error,
"precision is not integer"); "precision is not integer");
EXPECT_THROW_MSG(test_sprintf("%2$.*1$d"), format_error, "argument not found"); EXPECT_THROW_MSG(test_sprintf("%2$.*1$d"), format_error,
"argument not found");
EXPECT_THROW_MSG(test_sprintf("%2$.*1$d", big_num, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%2$.*1$d", big_num, 42), format_error,
"number is too big"); "number is too big");
} }
@ -322,7 +323,8 @@ TEST(printf_test, positional_width_and_precision) {
EXPECT_EQ(" 00042", test_sprintf("%3$*1$.*2$d", 7, 5, 42)); EXPECT_EQ(" 00042", test_sprintf("%3$*1$.*2$d", 7, 5, 42));
EXPECT_EQ(" ab", test_sprintf("%3$*1$.*2$s", 7, 2, "abcdef")); EXPECT_EQ(" ab", test_sprintf("%3$*1$.*2$s", 7, 2, "abcdef"));
EXPECT_EQ(" 00042", test_sprintf("%3$*1$.*2$x", 7, 5, 0x42)); EXPECT_EQ(" 00042", test_sprintf("%3$*1$.*2$x", 7, 5, 0x42));
EXPECT_EQ("100.4400000", test_sprintf("%6$-*5$.*4$f%3$s%2$s%1$s", "", "", "", 7, 4, 100.44)); EXPECT_EQ("100.4400000",
test_sprintf("%6$-*5$.*4$f%3$s%2$s%1$s", "", "", "", 7, 4, 100.44));
} }
template <typename T> struct make_signed { template <typename T> struct make_signed {
@ -555,3 +557,8 @@ TEST(printf_test, make_printf_args) {
fmt::vsprintf(fmt::basic_string_view<wchar_t>(L"[%d] %s happened"), fmt::vsprintf(fmt::basic_string_view<wchar_t>(L"[%d] %s happened"),
{fmt::make_printf_args<wchar_t>(n, L"something")})); {fmt::make_printf_args<wchar_t>(n, L"something")}));
} }
TEST(printf_test, trailing_percent_non_nul_terminated) {
auto p = std::unique_ptr<char>(new char('%'));
EXPECT_THROW(fmt::sprintf(fmt::string_view(p.get(), 1)), format_error);
}