https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81395
Jonathan Wakely <redi at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|UNCONFIRMED |NEW Last reconfirmed| |2017-07-11 Ever confirmed|0 |1 --- Comment #1 from Jonathan Wakely <redi at gcc dot gnu.org> --- (In reply to Jonathan Wakely from comment #0) > The problem is that the write calls overflow which starts with: Correction: the destructor calls overflow in this testcase. In the original testcase from a customer there was a write of BUFSIZ, which triggered overflow after filling the put area. A more direct way to reproduce it is by changing the final s << 'B'; to: s.write("B", 1); s.flush(); I can prevent the recursion with either: @@ -515,6 +515,7 @@ if (_M_reading) { _M_destroy_pback(); + _M_reading = false; const int __gptr_off = _M_get_ext_pos(_M_state_last); if (_M_seek(__gptr_off, ios_base::cur, _M_state_last) == pos_type(off_type(-1))) or: @@ -920,7 +925,7 @@ { // Part one: update the output sequence. bool __testvalid = true; - if (this->pbase() < this->pptr()) + if (this->pbase() < this->pptr() && __builtin_expect(!_M_reading, 1)) { const int_type __tmp = this->overflow(); if (traits_type::eq_int_type(__tmp, traits_type::eof())) But why are we getting into this state anyway? We have a non-empty output sequence when _M_reading is true, meaning we're in the middle of an uncommitted read. At the end of basic_filebuf::xsgetn after the read we do: if (__n == 0) { _M_set_buffer(0); _M_reading = true; } And _M_set_buffer(0) on a bidirectional filebuf sets up the put area: if (__testout && __off == 0 && _M_buf_size > 1 ) this->setp(_M_buf, _M_buf + _M_buf_size - 1); else this->setp(0, 0); This means the next write inserts straight into the put area, and then the next overflow() finds that _M_reading is true but there is also a pending output sequence.