From 9200553b229bbccbe24389998d06aa34065bc249 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 17 Jun 2026 08:36:43 +0200 Subject: [PATCH] Fix hang/assertion when printing to a pipe with closed read end (#4797) --- include/fmt/format-inl.h | 10 ++++++++-- test/format-test.cc | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 665f7308..ecd3ffec 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -1526,7 +1526,10 @@ template class file_base { FMT_THROW(system_error(errno, FMT_STRING("ungetc failed"))); } - void flush() { fflush(this->file_); } + void flush() { + if (fflush(this->file_) != 0) + FMT_THROW(system_error(errno, FMT_STRING("fflush failed"))); + } }; // A FILE wrapper for glibc. @@ -1572,7 +1575,10 @@ template class glibc_file : public file_base { return memchr(end, '\n', static_cast(size)); } - void flush() { fflush_unlocked(this->file_); } + void flush() { + if (fflush_unlocked(this->file_) != 0) + FMT_THROW(system_error(errno, FMT_STRING("fflush failed"))); + } }; // A FILE wrapper for Apple's libc. diff --git a/test/format-test.cc b/test/format-test.cc index 61a53c72..9a2258d5 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -18,6 +18,7 @@ #include // fegetexceptflag and FE_ALL_EXCEPT #include // INT_MAX #include // std::signbit +#include // std::signal, SIGPIPE #include // std::strlen #include // std::back_inserter #include // std::list @@ -2592,6 +2593,24 @@ TEST(format_test, invalid_glibc_buffer) { fmt::print(file, "------\n"); } + +TEST(format_test, print_to_broken_pipe) { + // Ignore SIGPIPE so that a failing write reports EPIPE instead of + // terminating the test process. It must stay ignored until the file is + // closed below because closing also flushes the remaining buffered data. + auto old_handler = std::signal(SIGPIPE, SIG_IGN); + { + auto pipe = fmt::pipe(); + pipe.read_end.close(); + auto write_end = pipe.write_end.fdopen("w"); + + // The data must exceed the file's buffer to force a flush during + // formatting, whose underlying write() then fails with EPIPE. + auto data = std::string(1024 * 1024, 'x'); + EXPECT_THROW(fmt::print(write_end.get(), "{}", data), std::system_error); + } + std::signal(SIGPIPE, old_handler); +} #endif // FMT_USE_FCNTL // Only defined after the test case.