On Fri, 23 Oct 2026 at 18:44, Jonathan Wakely <[email protected]> wrote:
>
> On Fri, 23 Oct 2026 at 18:40, Jonathan Wakely <[email protected]> wrote:
> >
> > From: Peter Edwards <[email protected]>
> >
> > If we fail to write the content of the buffer after inserting the
> > overflow character into the end of it, then remove that overflow
> > character before propagating the error back to the caller. Without this,
> > the caller is left with pptr set past epptr, and successive calls to
> > overflow will write further past the end of the managed buffer with each
> > failure.
> >
> > libstdc++-v3/ChangeLog:
> >
> > PR libstdc++/118099
> > * include/bits/fstream.tcc (basic_filebuf::overflow): Rewind the
> > put position if outputting the put area fails.
> > * testsuite/27_io/basic_filebuf/overflow/char/118099.cc: New
> > test.
> > * testsuite/27_io/basic_filebuf/overflow/wchar_t/118099.cc:
> > New test.
> >
> > Co-authored-by: Jonathan Wakely <[email protected]>
> > ---
> >
> > Tested x86_64-linux.
>
> I forgot to say:
> [PATCH 1/3] should be backported to active branches.
> [PATCH 2/3] is doesn't need to be backported since it only fixes a
> hypothetical problem.
> [PATCH 3/3] is not suitable for backporting, but the bug can be fixed
> with a try-catch around the call to _M_convert_to_external.
Or we could do this for the branches, which would fix the
non-hypothetical problems (and tests) fixed by PATCH 1/3 and 3/3:
--- a/libstdc++-v3/include/bits/fstream.tcc
+++ b/libstdc++-v3/include/bits/fstream.tcc
@@ -561,11 +561,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
if (this->pbase() < this->pptr())
{
+ struct _Guard {
+ ~_Guard() { if (_M_p) _M_p->pbump(-1); }
+ basic_filebuf* _M_p;
+ };
+ _Guard __g = { };
+
// If appropriate, append the overflow char.
if (!__testeof)
{
*this->pptr() = traits_type::to_char_type(__c);
this->pbump(1);
+ __g._M_p = this;
}
// Convert pending sequence to external representation,
@@ -573,6 +580,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
if (_M_convert_to_external(this->pbase(),
this->pptr() - this->pbase()))
{
+ __g._M_p = 0;
_M_set_buffer(0);
__ret = traits_type::not_eof(__c);
}
For trunk I would still like to do the larger refactoring so that we
avoid advancing then conditionally rewinding the next pointer, and the
changes to _M_convert_to_external make it simpler IMHO.