diff mbox series

[_GLIBCXX_DEBUG] Enhance detection of invalid iterators usage

Message ID b2b815ec-2d67-f729-7ea9-61f72bcf6350@gmail.com
State New
Headers show
Series [_GLIBCXX_DEBUG] Enhance detection of invalid iterators usage | expand

Commit Message

François Dumont Feb. 1, 2021, 6:43 p.m. UTC
At the moment some iterators like std::list<>::end() looks like 
value-init iterators once detached.

I think using an iterator in such a state is wrong so here is a patch to 
detect this.

This patch also add a new iterator state: singular (value-initialized)

Example of the output of the forward_list/iterator2_neg.cc test:

In function:
     bool __gnu_debug::operator!=(const _Self&, const _Self&)

Error: attempt to compare a singular iterator to a
singular (value-initialized) iterator.

Objects involved in the operation:
     iterator "__lhs" @ 0x0x7fffb777d4b0 {
       type = std::__cxx1998::_Fwd_list_iterator<int> (mutable iterator);
       state = singular;
     }
     iterator "__rhs" @ 0x0x7fffb777d4e0 {
       type = std::__cxx1998::_Fwd_list_iterator<int> (mutable iterator);
       state = singular (value-initialized);
     }

     libstdc++: [_GLIBCXX_DEBUG] Do not consider detach iterators as 
value-initialized

     An attach iterator has its _M_version set to something != 0. This 
value shall be preserved
     when detaching it so that the iterator does not look like a value 
initialized one.

     libstdc++-v3/ChangeLog:

             * include/debug/formatter.h (__singular_value_init): New 
_Iterator_state enum entry.
             (_Parameter<>(const _Safe_iterator<>&, const char*, 
_Is_iterator)): Check if iterator
             parameter is value-initialized.
             (_Parameter<>(const _Safe_local_iterator<>&, const char*, 
_Is_iterator)): Likewise.
             * include/debug/safe_iterator.h 
(_Safe_iterator<>::_M_value_initialized()): New. Adapt
             checks.
             * include/debug/safe_local_iterator.h 
(_Safe_local_iterator<>::_M_value_initialized()): New.
             Adapt checks.
             * src/c++11/debug.cc (_Safe_iterator_base::_M_reset): Do 
not reset _M_version.
             (print_field(PrintContext&, const _Parameter&, const 
char*)): Adapt state_names.
             * testsuite/23_containers/deque/debug/iterator1_neg.cc: New 
test.
             * testsuite/23_containers/deque/debug/iterator2_neg.cc: New 
test.
             * 
testsuite/23_containers/forward_list/debug/iterator1_neg.cc: New test.
             * 
testsuite/23_containers/forward_list/debug/iterator2_neg.cc: New test.

Tested under Linux x86_64.

Ok to commit ?

François
diff mbox series

Patch

diff --git a/libstdc++-v3/include/debug/formatter.h b/libstdc++-v3/include/debug/formatter.h
index 7140fed5e83..1b96de07261 100644
--- a/libstdc++-v3/include/debug/formatter.h
+++ b/libstdc++-v3/include/debug/formatter.h
@@ -185,6 +185,7 @@  namespace __gnu_debug
       __rbegin,		// dereferenceable, and at the reverse-beginning
       __rmiddle,	// reverse-dereferenceable, not at the reverse-beginning
       __rend,		// reverse-past-the-end
+      __singular_value_init,	// singular, value initialized
       __last_state
     };
 
@@ -278,7 +279,12 @@  namespace __gnu_debug
 	  _M_variant._M_iterator._M_seq_type = _GLIBCXX_TYPEID(_Sequence);
 
 	  if (__it._M_singular())
-	    _M_variant._M_iterator._M_state = __singular;
+	    {
+	      if (__it._M_value_initialized())
+		_M_variant._M_iterator._M_state = __singular_value_init;
+	      else
+		_M_variant._M_iterator._M_state = __singular;
+	    }
 	  else
 	    {
 	      if (__it._M_is_before_begin())
@@ -306,7 +312,12 @@  namespace __gnu_debug
 	  _M_variant._M_iterator._M_seq_type = _GLIBCXX_TYPEID(_Sequence);
 
 	  if (__it._M_singular())
-	    _M_variant._M_iterator._M_state = __singular;
+	    {
+	      if (__it._M_value_initialized())
+		_M_variant._M_iterator._M_state = __singular_value_init;
+	      else
+		_M_variant._M_iterator._M_state = __singular;
+	    }
 	  else
 	    {
 	      if (__it._M_is_end())
diff --git a/libstdc++-v3/include/debug/safe_iterator.h b/libstdc++-v3/include/debug/safe_iterator.h
index a10df190969..99d8830e45e 100644
--- a/libstdc++-v3/include/debug/safe_iterator.h
+++ b/libstdc++-v3/include/debug/safe_iterator.h
@@ -41,8 +41,8 @@ 
 
 #define _GLIBCXX_DEBUG_VERIFY_OPERANDS(_Lhs, _Rhs, _BadMsgId, _DiffMsgId) \
   _GLIBCXX_DEBUG_VERIFY(!_Lhs._M_singular() && !_Rhs._M_singular()	\
-			|| (_Lhs.base() == _Iterator()			\
-			    && _Rhs.base() == _Iterator()),		\
+			|| (_Lhs._M_value_initialized()			\
+			    && _Rhs._M_value_initialized()),		\
 			_M_message(_BadMsgId)				\
 			._M_iterator(_Lhs, #_Lhs)			\
 			._M_iterator(_Rhs, #_Rhs));			\
@@ -177,7 +177,7 @@  namespace __gnu_debug
 	// _GLIBCXX_RESOLVE_LIB_DEFECTS
 	// DR 408. Is vector<reverse_iterator<char*> > forbidden?
 	_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
-			      || __x.base() == _Iterator(),
+			      || __x._M_value_initialized(),
 			      _M_message(__msg_init_copy_singular)
 			      ._M_iterator(*this, "this")
 			      ._M_iterator(__x, "other"));
@@ -193,7 +193,7 @@  namespace __gnu_debug
       : _Iter_base()
       {
 	_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
-			      || __x.base() == _Iterator(),
+			      || __x._M_value_initialized(),
 			      _M_message(__msg_init_copy_singular)
 			      ._M_iterator(*this, "this")
 			      ._M_iterator(__x, "other"));
@@ -220,7 +220,7 @@  namespace __gnu_debug
 	  // _GLIBCXX_RESOLVE_LIB_DEFECTS
 	  // DR 408. Is vector<reverse_iterator<char*> > forbidden?
 	  _GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
-				|| __x.base() == _MutableIterator(),
+				|| __x._M_value_initialized(),
 				_M_message(__msg_init_const_singular)
 				._M_iterator(*this, "this")
 				._M_iterator(__x, "other"));
@@ -236,7 +236,7 @@  namespace __gnu_debug
 	// _GLIBCXX_RESOLVE_LIB_DEFECTS
 	// DR 408. Is vector<reverse_iterator<char*> > forbidden?
 	_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
-			      || __x.base() == _Iterator(),
+			      || __x._M_value_initialized(),
 			      _M_message(__msg_copy_singular)
 			      ._M_iterator(*this, "this")
 			      ._M_iterator(__x, "other"));
@@ -266,7 +266,7 @@  namespace __gnu_debug
       operator=(_Safe_iterator&& __x) noexcept
       {
 	_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
-			      || __x.base() == _Iterator(),
+			      || __x._M_value_initialized(),
 			      _M_message(__msg_copy_singular)
 			      ._M_iterator(*this, "this")
 			      ._M_iterator(__x, "other"));
@@ -403,6 +403,11 @@  namespace __gnu_debug
       _M_incrementable() const
       { return !this->_M_singular() && !_M_is_end(); }
 
+      /// Is the iterator value-initialized?
+      bool
+      _M_value_initialized() const
+      { return _M_version == 0 && base() == _Iter_base(); }
+
       // Can we advance the iterator @p __n steps (@p __n may be negative)
       bool
       _M_can_advance(difference_type __n, bool __strict = false) const;
diff --git a/libstdc++-v3/include/debug/safe_local_iterator.h b/libstdc++-v3/include/debug/safe_local_iterator.h
index 31c48e65a24..97ae56c5a7c 100644
--- a/libstdc++-v3/include/debug/safe_local_iterator.h
+++ b/libstdc++-v3/include/debug/safe_local_iterator.h
@@ -33,8 +33,8 @@ 
 
 #define _GLIBCXX_DEBUG_VERIFY_OPERANDS(_Lhs, _Rhs) \
   _GLIBCXX_DEBUG_VERIFY(!_Lhs._M_singular() && !_Rhs._M_singular()	\
-			|| (_Lhs.base() == _Iterator{}			\
-			    && _Rhs.base() == _Iterator{}),		\
+			|| (_Lhs._M_value_initialized()			\
+			    && _Rhs._M_value_initialized()),		\
 			_M_message(__msg_iter_compare_bad)		\
 			._M_iterator(_Lhs, "lhs")			\
 			._M_iterator(_Rhs, "rhs"));			\
@@ -127,7 +127,7 @@  namespace __gnu_debug
 	// _GLIBCXX_RESOLVE_LIB_DEFECTS
 	// DR 408. Is vector<reverse_iterator<char*> > forbidden?
 	_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
-			      || __x.base() == _Iterator(),
+			      || __x._M_value_initialized(),
 			      _M_message(__msg_init_copy_singular)
 			      ._M_iterator(*this, "this")
 			      ._M_iterator(__x, "other"));
@@ -142,7 +142,7 @@  namespace __gnu_debug
       : _Iter_base()
       {
 	_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
-			      || __x.base() == _Iterator(),
+			      || __x._M_value_initialized(),
 			      _M_message(__msg_init_copy_singular)
 			      ._M_iterator(*this, "this")
 			      ._M_iterator(__x, "other"));
@@ -167,7 +167,7 @@  namespace __gnu_debug
 	  // _GLIBCXX_RESOLVE_LIB_DEFECTS
 	  // DR 408. Is vector<reverse_iterator<char*> > forbidden?
 	  _GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
-				|| __x.base() == _MutableIterator(),
+				|| __x._M_value_initialized(),
 				_M_message(__msg_init_const_singular)
 				._M_iterator(*this, "this")
 				._M_iterator(__x, "other"));
@@ -183,7 +183,7 @@  namespace __gnu_debug
 	// _GLIBCXX_RESOLVE_LIB_DEFECTS
 	// DR 408. Is vector<reverse_iterator<char*> > forbidden?
 	_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
-			      || __x.base() == _Iterator(),
+			      || __x._M_value_initialized(),
 			      _M_message(__msg_copy_singular)
 			      ._M_iterator(*this, "this")
 			      ._M_iterator(__x, "other"));
@@ -212,7 +212,7 @@  namespace __gnu_debug
       operator=(_Safe_local_iterator&& __x) noexcept
       {
 	_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
-			      || __x.base() == _Iterator(),
+			      || __x._M_value_initialized(),
 			      _M_message(__msg_copy_singular)
 			      ._M_iterator(*this, "this")
 			      ._M_iterator(__x, "other"));
@@ -343,6 +343,11 @@  namespace __gnu_debug
       _M_incrementable() const
       { return !this->_M_singular() && !_M_is_end(); }
 
+      /// Is the iterator value-initialized?
+      bool
+      _M_value_initialized() const
+      { return _M_version == 0 && base() == _Iter_base{}; }
+
       // Is the iterator range [*this, __rhs) valid?
       bool
       _M_valid_range(const _Safe_local_iterator& __rhs,
diff --git a/libstdc++-v3/src/c++11/debug.cc b/libstdc++-v3/src/c++11/debug.cc
index 5a642097d17..5e446b96df8 100644
--- a/libstdc++-v3/src/c++11/debug.cc
+++ b/libstdc++-v3/src/c++11/debug.cc
@@ -411,7 +411,8 @@  namespace __gnu_debug
   _M_reset() throw ()
   {
     __atomic_store_n(&_M_sequence, (_Safe_sequence_base*)0, __ATOMIC_RELEASE);
-    _M_version = 0;
+    // Detach iterator shall not look like a value-initialized one.
+    // _M_version = 0;
     _M_prior = 0;
     _M_next = 0;
   }
@@ -737,7 +738,8 @@  namespace
 		"before-begin",
 		"dereferenceable (start-of-reverse-sequence)",
 		"dereferenceable (reverse)",
-		"past-the-reverse-end"
+		"past-the-reverse-end",
+		"singular (value-initialized)"
 	      };
 	    print_word(ctx, state_names[iterator._M_state]);
 	  }
diff --git a/libstdc++-v3/testsuite/23_containers/deque/debug/iterator1_neg.cc b/libstdc++-v3/testsuite/23_containers/deque/debug/iterator1_neg.cc
new file mode 100644
index 00000000000..faf68dfce3b
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/deque/debug/iterator1_neg.cc
@@ -0,0 +1,39 @@ 
+// Copyright (C) 2021 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+//
+// { dg-do run { xfail *-*-* } }
+// { dg-require-debug-mode "" }
+
+#include <deque>
+
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  typedef typename std::deque<int>::iterator It;
+  std::deque<int> dq;
+  dq.push_back(1);
+
+  It it = It();
+  VERIFY( dq.begin() != it );
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/deque/debug/iterator2_neg.cc b/libstdc++-v3/testsuite/23_containers/deque/debug/iterator2_neg.cc
new file mode 100644
index 00000000000..ae2247312dd
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/deque/debug/iterator2_neg.cc
@@ -0,0 +1,42 @@ 
+// Copyright (C) 2021 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+//
+// { dg-do run { xfail *-*-* } }
+// { dg-require-debug-mode "" }
+
+#include <deque>
+
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  typedef typename std::deque<int>::iterator It;
+  It it;
+  {
+    std::deque<int> dq;
+    it = dq.begin();
+  }
+
+  It value_init_it = It();
+  VERIFY( it != value_init_it );
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/debug/iterator1_neg.cc b/libstdc++-v3/testsuite/23_containers/forward_list/debug/iterator1_neg.cc
new file mode 100644
index 00000000000..fcb6f9e23a6
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/debug/iterator1_neg.cc
@@ -0,0 +1,39 @@ 
+// Copyright (C) 2021 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+//
+// { dg-do run { xfail *-*-* } }
+// { dg-require-debug-mode "" }
+
+#include <forward_list>
+
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  typedef typename std::forward_list<int>::iterator It;
+  std::forward_list<int> fl;
+  fl.push_front(1);
+
+  It it = It();
+  VERIFY( fl.begin() != it );
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/debug/iterator2_neg.cc b/libstdc++-v3/testsuite/23_containers/forward_list/debug/iterator2_neg.cc
new file mode 100644
index 00000000000..c350327ed37
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/debug/iterator2_neg.cc
@@ -0,0 +1,42 @@ 
+// Copyright (C) 2021 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+//
+// { dg-do run { target c++11 xfail *-*-* } }
+// { dg-require-debug-mode "" }
+
+#include <forward_list>
+
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  typedef typename std::forward_list<int>::iterator It;
+  It it;
+  {
+    std::forward_list<int> fl;
+    it = fl.begin();
+  }
+
+  It value_init_it{};
+  VERIFY( it != value_init_it );
+}
+
+int main()
+{
+  test01();
+  return 0;
+}