[patch] proposed fix for libstdc++/59829

Jonathan Wakely jwakely@redhat.com
Mon Jan 27 19:38:00 GMT 2014


This is the best I've come up with to avoid dereferencing an invalid
pointer when calling vector::data() on an empty vector.

For C++03 we reurn the vector's pointer type, so can just return the
internal pointer, but for C++11 we need to convert that to a raw
pointer, which we do by dereferencing, so we must check if it's valid
first.

Any better suggestions before I commit this?
(Tests pass, although I need to adjust some dg-error line numbers
before committing it.)


-------------- next part --------------
commit 60f9bb3b06ea95054d3fb442c9c212e7afe66acc
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Mon Jan 27 18:35:17 2014 +0000

    	PR libstdc++/59829
    	* include/bits/stl_vector.h (vector::data()): Do not dereference
    	pointers when empty.
    	* testsuite/23_containers/vector/59829.cc: New.

diff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/include/bits/stl_vector.h
index f482957..8393242 100644
--- a/libstdc++-v3/include/bits/stl_vector.h
+++ b/libstdc++-v3/include/bits/stl_vector.h
@@ -880,19 +880,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        */
 #if __cplusplus >= 201103L
       _Tp*
-#else
-      pointer
-#endif
       data() _GLIBCXX_NOEXCEPT
-      { return std::__addressof(front()); }
+      { return empty() ? nullptr : std::__addressof(*this->_M_impl._M_start); }
 
-#if __cplusplus >= 201103L
       const _Tp*
+      data() const _GLIBCXX_NOEXCEPT
+      { return empty() ? nullptr : std::__addressof(*this->_M_impl._M_start); }
 #else
+      pointer
+      data() _GLIBCXX_NOEXCEPT
+      { return this->_M_impl._M_start; }
+
       const_pointer
-#endif
       data() const _GLIBCXX_NOEXCEPT
-      { return std::__addressof(front()); }
+      { return this->_M_impl._M_start; }
+#endif
 
       // [23.2.4.3] modifiers
       /**
diff --git a/libstdc++-v3/testsuite/23_containers/vector/59829.cc b/libstdc++-v3/testsuite/23_containers/vector/59829.cc
new file mode 100644
index 0000000..8d9f39d
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/vector/59829.cc
@@ -0,0 +1,108 @@
+// Copyright (C) 2014 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-options "-std=gnu++11" }
+
+// libstdc++/59829
+
+#include <vector>
+
+// User-defined pointer type that throws if a null pointer is dereferenced.
+template<typename T>
+struct Pointer
+{
+  typedef T element_type;
+
+  // typedefs for iterator_traits
+  typedef T value_type;
+  typedef std::ptrdiff_t difference_type;
+  typedef std::random_access_iterator_tag iterator_category;
+  typedef Pointer pointer;
+  typedef T& reference;
+
+  T* value;
+
+  explicit Pointer(T* p = nullptr) : value(p) { }
+
+  template<typename U, typename Requires = decltype((T*)std::declval<U*>())>
+    Pointer(const Pointer<U>& p) : value(p.value) { }
+
+  T& operator*() const
+  {
+    if (!value)
+      throw nullptr;
+    return *value;
+  }
+
+  T* operator->() const { return value; }
+
+  Pointer& operator++() { ++value; return *this; }
+  Pointer operator++(int) { Pointer tmp(*this); ++value; return tmp; }
+  Pointer& operator--() { --value; return *this; }
+  Pointer operator--(int) { Pointer tmp(*this); --value; return tmp; }
+
+  Pointer& operator+=(difference_type n) { value += n; return *this; }
+  Pointer& operator-=(difference_type n) { value -= n; return *this; }
+
+  explicit operator bool() const { return value != nullptr; }
+
+  Pointer operator+(difference_type n) { Pointer p(*this); return p += n; }
+  Pointer operator-(difference_type n) { Pointer p(*this); return p -= n; }
+};
+
+template<typename T>
+std::ptrdiff_t operator-(Pointer<T> l, Pointer<T> r)
+{ return l.value - r.value; }
+
+template<typename T>
+bool operator==(Pointer<T> l, Pointer<T> r)
+{ return l.value == r.value; }
+
+template<typename T>
+bool operator!=(Pointer<T> l, Pointer<T> r)
+{ return l.value != r.value; }
+
+template<typename T>
+struct Alloc
+{
+  typedef T value_type;
+  typedef Pointer<T> pointer;
+
+  Alloc() = default;
+  template<typename U>
+    Alloc(const Alloc<U>&) { }
+
+  pointer allocate(std::size_t n)
+  { return pointer(std::allocator<T>().allocate(n)); }
+
+  void deallocate(pointer p, std::size_t n)
+  { std::allocator<T>().deallocate(p.value, n); }
+};
+
+template<typename T>
+bool operator==(Alloc<T> l, Alloc<T> r)
+{ return true; }
+
+template<typename T>
+bool operator!=(Alloc<T> l, Alloc<T> r)
+{ return false; }
+
+int main()
+{
+  std::vector<int, Alloc<int>> a;
+  a.data();
+}


More information about the Gcc-patches mailing list