This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

PR libstdc++/60587


I'm debugging http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60587 and
have found a number of problems.

Firstly, the bug report is correct, this overload dereferences the
__other argument without checking if that is OK:

  template<typename _Iterator, typename _Sequence, typename _InputIterator>
    inline bool
    __foreign_iterator_aux3(const _Safe_iterator<_Iterator, _Sequence>& __it,
          _InputIterator __other,
          std::true_type)

Secondly, in this testcase we should never even have reached that
overload, because we should have gone to this overload of _aux2:

  template<typename _Iterator, typename _Sequence, typename _OtherIterator>
    inline bool
    __foreign_iterator_aux2(const _Safe_iterator<_Iterator, _Sequence>& __it,
    const _Safe_iterator<_OtherIterator, _Sequence>& __other,
    std::input_iterator_tag)
    { return __it._M_get_sequence() != __other._M_get_sequence(); }

However that is not chosen by overload resolution because this is a better
match when __other is non-const:

  template<typename _Iterator, typename _Sequence, typename _InputIterator>
    inline bool
    __foreign_iterator_aux2(const _Safe_iterator<_Iterator, _Sequence>& __it,
          _InputIterator __other,
          std::random_access_iterator_tag)

Fixing the overload resolution bug makes the testcase in the PR pass,
but the underlying problem of dereferencing an invalid iterator still
exists and can be shown by changing the testcase slightly:

#define _GLIBCXX_DEBUG
#include <vector>
int main() {
    std::vector<int> a;
    std::vector<long> b;
    a.push_back(1);
    a.insert(a.end(), b.begin(), b.end());
}

That still dereferences b.begin(), but that too can be fixed (either
as suggested in the PR or by passing the begin and end iterators into
the __foreign_iter function) but I think there's still another
problem.

I'm looking again at the code that attempts to check if we have
contiguous storage:

  if (std::addressof(*(__it._M_get_sequence()->_M_base().end() - 1))
      - std::addressof(*(__it._M_get_sequence()->_M_base().begin()))
      == __it._M_get_sequence()->size() - 1)

Are we really sure that ensures contiguous iterators? What if we have
a deque with three blocks laid out in memory like this:
1XXXXXXX........3XXxxxxx................2XXXXXXX
 ^                  ^
 begin()            end()

1 is the start of the first block, 2 is the start of the second block
and 3 is the start of the third block.
X is an element, x is reserved but uninitialized capacity
. is unallocated memory (or memory not used by the deque)

Here we have end() - begin() == size() but non-contiguous memory.
If the __other iterator happens to point to the unallocated memory
between 1 and 3 then it will appear to be part of the deque, but
isn't.

I think the safe thing to do is (as I suggested at the time) to have a
trait saying which iterator types refer to contiguous memory. Our
debug mode only supports our own containers, so the ones which are
contiguous are known.  For 4.9.0 I think the right option is simply
to remove __foreign_iterator_aux3 and __foreign_iterator_aux4
completely. The fixed version of __foreign_iterator_aux2() can detect
when we have iterators referring to the same sequence, which is what
we really want to detect. That's what the attached patch does and what
I'm going to test.


Attachment: patch.txt
Description: Text document


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]