From 4daaa87b8649548d0e65a1b33574e3c024dbd3e4 Mon Sep 17 00:00:00 2001 From: aizu-m Date: Sat, 7 Nov 2026 22:44:57 +0530 Subject: [PATCH] fix heap overflow in fmt::ostream with buffer_size = 0 (#5011) output_file(path, buffer_size = 0) allocated a zero-size buffer, but ostream::grow can only make room by flushing, so a zero capacity is never increased. push_back then writes past the allocation and leaves size() above capacity(), after which append computes capacity_ - size_ as SIZE_MAX and copies the rest of the output out of bounds. If the first write is an append instead, the count is clamped to 0 and print never returns. Allocate at least one byte so a zero buffer size behaves like 1. --- src/os.cc | 4 +++- test/os-test.cc | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/os.cc b/src/os.cc index 4840a658..f0e71aac 100644 --- a/src/os.cc +++ b/src/os.cc @@ -383,7 +383,9 @@ void ostream::grow(buffer& buf, size_t) { ostream::ostream(cstring_view path, const detail::ostream_params& params) : buffer(grow), file_(path, params.oflag) { - set(new char[params.buffer_size], params.buffer_size); + // grow() can only make room by flushing, so the buffer can't be empty. + size_t size = max_of(params.buffer_size, 1); + set(new char[size], size); } ostream::ostream(ostream&& other) noexcept diff --git a/test/os-test.cc b/test/os-test.cc index 5bc9f7eb..24f96ed9 100644 --- a/test/os-test.cc +++ b/test/os-test.cc @@ -295,6 +295,15 @@ TEST(ostream_test, buffer_size) { EXPECT_READ(in, "foo"); } +TEST(ostream_test, zero_buffer_size) { + auto test_file = uniq_file_name(__LINE__); + fmt::ostream out = fmt::output_file(test_file, fmt::buffer_size = 0); + out.print("{}{}", 'f', "oo"); + out.close(); + file in(test_file, file::RDONLY); + EXPECT_READ(in, "foo"); +} + TEST(ostream_test, truncate) { auto test_file = uniq_file_name(__LINE__); {