https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118099
Bug ID: 118099 Summary: basic_filebuf::overflow is left inconsistent on I/O error. Product: gcc Version: 14.2.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: peadar at arista dot com Target Milestone: --- Created attachment 59904 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=59904&action=edit Naive patch for issue. The following illustrates the problem directly: ``` #include <bits/stdc++.h> int main(int argc, char *argv[]) { std::ofstream of("/dev/full", std::ios_base::out); auto rdbuf = of.rdbuf(); for (;;) rdbuf->sputc('x'); } ``` The same problem manifests writing to std::cout if you set `std::ios_base::sync_with_stdio(false)` when the buffer fills, sputc calls `std::basic_filebuf<char, std::char_traits<char> >::overflow`, passing the character as the 'overflow' character. basic_filebuf maintains space in the managed buffer for this overflow, and inserts it into the buffer via *pptr()/pbump(1) - about line 560 of fstream.tcc ``` if (!__testeof) { *this->pptr() = traits_type::to_char_type(__c); this->pbump(1); } ``` However, if the call to _M_convert_to_external fails (eventually due to the write failing, in the above case because we fail to write to /dev/full), then an error is returned, but the character is left sitting in the buffer, and pptr > epptr. A further call to sputc will hit the same point, and we will continue to write further past the end of the managed buffer. Of course the caller should check for an error, but even if they do, the streambuf is now in an inconsistent state, and there's no obvious approach to recover - regardless of the memory scribble, we're returning with an error from sputc(), but have also inserted the character we "failed" to write into the buffer, which is inconsistent with the error return. So the simplest thing to do here seems to be to just remove the character from the buffer . Dumb patch attached, for what its worth. This doesn't happen with std::cout if it's tied to C's stdout - the buffer is managed differently and the problem doesn't manifest.