On Fri, 27 Feb 2026 at 17:37, Tomasz Kaminski <[email protected]> wrote:
>
>
>
> On Fri, Feb 27, 2026 at 5:12 PM Tomasz Kaminski <[email protected]> wrote:
>>
>>
>>
>> On Fri, Feb 27, 2026 at 4:48 PM Jonathan Wakely <[email protected]> wrote:
>>>
>>> On Wed, 07 Jan 2026 at 15:30 +0100, Tomasz Kamiński wrote:
>>> >The warning was produced by following seqeunce, given an 
>>> >istream_iterator<char>
>>> >it, such that *it will result in hitting EoF in it->_M_get(), and thus 
>>> >clearing
>>> >_M_sbuf, the subsequent call to ++it, will result in _M_sbuf->sbumpc() call
>>> >on null pointer dereference. This is however an false-positive, as in such 
>>> >sitution
>>> >it == istream_iteator() returns true, and the iterator should not be 
>>> >dereferenced
>>> >in first place.
>>> >
>>> >This patch addresses the above by clearing the _M_sbuf in operator++, 
>>> >instead of
>>> >_M_get(). This removes the need for making _M_sbuf mutable, and thus make 
>>> >the
>>> >implementation conforming with regards to C++11 [res.on.data.races] p3.
>>> >
>>> >This change should not or positive have performance impact on the usual 
>>> >iteration
>>>
>>> I think this should be:
>>>
>>> "This change should have zero or positive performance impact ..."
>>>
>>> >patterns, in form:
>>> > while (it != end) { process(*it); ++it; }
>>> >In case when it is end-of-stream iterator, the it != end returns in one 
>>> >call of
>>> >_M_sbuf->sgetc() both before and after the change. However we do not 
>>> >modify _M_sbuf
>>> >in this case. For non-empty range, we replace call to _M_sbuf->sbumpc() 
>>> >with
>>> >_M_sbuf->snextc() in pre-increment, and extract the check again from *it to
>>> >++it. However, as _M_sbuf is now cleared during increment, so last it != 
>>> >end
>>> >check avoids _M_sbuf->sgetc() call to check against EoF.
>>> >
>>> >However, this change impact the behavior of the post-increment (*it++), as
>>> >we now load both current character (for return value) and next character 
>>> >(to
>>> >check against EoF). In consequence we call both sgetc() and snextc(), in 
>>> >contrast
>>> >to previous single sbumpc() call.
>>> >
>>> >       PR libstdc++/105580
>>> >
>>> >libstdc++-v3/ChangeLog:
>>> >
>>> >       * include/bits/streambuf_iterator.h (istreambuf_iterator::_M_sbuf):
>>> >       Remove mutable and adjust whitespace.
>>> >       (istreambuf_iterator::_M_c): Adjust whitespace.
>>> >       (istreambuf_iterator::operator++()): Clear _M_sbuf if next character
>>> >       is EoF.
>>> >       (istreambuf_iterator::operator++(int)): Use _M_sbuf->sgetc() to
>>> >       load current character, and define in terms of __*this.
>>>
>>> This should be ++ not __
>>>
>>> >       (istreambuf_iterator::_M_get()): Do not clear _M_sbuf in case of 
>>> > EoF.
>>> >       * testsuite/24_iterators/istreambuf_iterator/2.cc: Test for using
>>> >       multiple iterators to same rdbuf.
>>> >---
>>> >This warning turned out to be false-positive (as explained in the commit
>>> >decription), so we can just add add pragrams to address that. But this
>>> >change seem to be beneficial anyway, as we are no longer causing
>>> >data-races.
>>> >
>>> >Tested on x86_64-linux. Locally tested with -Wnull-dereference,
>>> >and the istream_iterator warning in 24_iterators/istreambuf_iterator/2.cc
>>> >disappears (there are some other warning present I haven't looked into
>>> >that yet).
>>>
>>> Can we add a new testsuite/24_iterators/istreambuf_iterator/105580.cc
>>> using exactly the testcase from the bug, with -Wnull-dereference in
>>> the dg-options? I don't think the additions to 2.cc cover it. I'd like
>>> us to have a test that gives the warning today and doesn't warn after
>>> the fix.
>>
>> OK will do.
>>>
>>>
>>> > .../include/bits/streambuf_iterator.h         | 20 +++----
>>> > .../24_iterators/istreambuf_iterator/2.cc     | 58 +++++++++++++++++++
>>> > 2 files changed, 66 insertions(+), 12 deletions(-)
>>> >
>>> >diff --git a/libstdc++-v3/include/bits/streambuf_iterator.h 
>>> >b/libstdc++-v3/include/bits/streambuf_iterator.h
>>> >index 93d3dd24f93..095928ca4d8 100644
>>> >--- a/libstdc++-v3/include/bits/streambuf_iterator.h
>>> >+++ b/libstdc++-v3/include/bits/streambuf_iterator.h
>>> >@@ -112,8 +112,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>> >       // the "end of stream" iterator value.
>>> >       // NB: This implementation assumes the "end of stream" value
>>> >       // is EOF, or -1.
>>> >-      mutable streambuf_type* _M_sbuf;
>>> >-      int_type                        _M_c;
>>> >+      streambuf_type* _M_sbuf;
>>>
>>> I've been trying to figure out if this change (removing 'mutable')
>>> could be unsafe if we mix objects compiled with old and new GCC.
>>>
>>> In a TU compiled by an old GCC, _M_sbuf could change by changed when
>>> the iterator reaches EOF. In a TU compiled by the new GCC, could the
>>> compiler optimize based on the fact that if only const member
>>> functionss are used, the member can't change?
>>>
>>> I don't think it's a problem, because the new compiler has to assume
>>> that any non-inline code could already do a const_cast on the iterator
>>> and so call non-const members that way. The only way it can optimize
>>> by assuming _M_sbuf never, ever changes is if it can see the original
>>> definition of the iterator and it was defined as const. But in that
>>> case, nobody can ever call non-const members on it anyway.
>>
>> I thought about it, and the only situation when removing the mutable could 
>> have
>> any impact, is that you declare an iterator as a const object, so the 
>> compiler can in
>> that case now can propagate _M_sbuf value. But in that case, there is no way 
>> to
>> mutate _M_sbuf without UB.
>>
>>>
>>>
>>> So I think this is safe. I would feel more comfortable if we left the
>>> 'mutable' for GCC 16 with a comment saying it's not needed now, and
>>> then remove it for GCC 17 early in stage 1.
>>
>> Given that the warning is FP, and is emitted from system headers, do we even
>> need to have fix for GCC16? Maybe I should simply add pragma to ignore 
>> warnings
>> for now, and postpone the whole endeavor for GCC17? Or just leave the 
>> warning as is.
>> is emitted from system headers anyway.
>
> I also feel uneasy about touching istream iterator now. In the beginning, we 
> prioritized the
> change because we thought this was a true bug, so I think a better approach 
> now may be to
> add test emmiting and pragmas disabling the warning now (with backports)
>  and land the changes to GCC17.

OK, that seems fine (and also safe for backports to older branches) as
long as putting the pragmas in istreambuf_iterator actually fixes it.

The warnings come from here, not from the iterator:

/home/jwakely/gcc/16/include/c++/16.0.1/streambuf:494:30: warning:
potential null pointer dereference [-Wnull-dereference]
 494 |       gptr()  const { return _M_in_cur;  }
     |                              ^~~~~~~~~

So I'm not sure if putting pragmas in the iterator will prevent that.

Reply via email to