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.

Reply via email to