re PR libstdc++/78389 (list::merge and list::sort are not exception safe)
authorVille Voutilainen <ville.voutilainen@gmail.com>
Fri, 13 Jan 2017 14:46:25 +0000 (16:46 +0200)
committerVille Voutilainen <ville@gcc.gnu.org>
Fri, 13 Jan 2017 14:46:25 +0000 (16:46 +0200)
PR libstdc++/78389
* include/bits/list.tcc (merge(list&&)):
Adjust list sizes if the comparator throws.
(merge(list&&, _StrictWeakOrdering)): Likewise.
(sort()): Splice elements back from the scratch buffers
if the comparator throws.
(sort(_StrictWeakOrdering)): Likewise.
* testsuite/23_containers/list/operations/78389.cc: New.

From-SVN: r244439

libstdc++-v3/ChangeLog
libstdc++-v3/include/bits/list.tcc
libstdc++-v3/testsuite/23_containers/list/operations/78389.cc [new file with mode: 0644]

index 55fb708de1bca9451b30a59a2bffeb68fe8faa8c..76d86fbf705d6e6b4e7651d95eedd4b98e72f380 100644 (file)
@@ -1,3 +1,14 @@
+2017-01-13  Ville Voutilainen  <ville.voutilainen@gmail.com>
+
+       PR libstdc++/78389
+       * include/bits/list.tcc (merge(list&&)):
+       Adjust list sizes if the comparator throws.
+       (merge(list&&, _StrictWeakOrdering)): Likewise.
+       (sort()): Splice elements back from the scratch buffers
+       if the comparator throws.
+       (sort(_StrictWeakOrdering)): Likewise.
+       * testsuite/23_containers/list/operations/78389.cc: New.
+
 2017-01-13  Jonathan Wakely  <jwakely@redhat.com>
 
        * testsuite/23_containers/unordered_set/allocator/ext_ptr.cc: Mark
index c4f397feea998642e7eb4b9862210026a9c548d6..5be49a839972b4e780b9fb55ebd6d8d68db0d2fc 100644 (file)
@@ -380,26 +380,36 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       // 300. list::merge() specification incomplete
       if (this != std::__addressof(__x))
        {
-         _M_check_equal_allocators(__x); 
+         _M_check_equal_allocators(__x);
 
          iterator __first1 = begin();
          iterator __last1 = end();
          iterator __first2 = __x.begin();
          iterator __last2 = __x.end();
-         while (__first1 != __last1 && __first2 != __last2)
-           if (*__first2 < *__first1)
-             {
-               iterator __next = __first2;
-               _M_transfer(__first1, __first2, ++__next);
-               __first2 = __next;
-             }
-           else
-             ++__first1;
-         if (__first2 != __last2)
-           _M_transfer(__last1, __first2, __last2);
+         const size_t __orig_size = __x.size();
+         __try {
+           while (__first1 != __last1 && __first2 != __last2)
+             if (*__first2 < *__first1)
+               {
+                 iterator __next = __first2;
+                 _M_transfer(__first1, __first2, ++__next);
+                 __first2 = __next;
+               }
+             else
+               ++__first1;
+           if (__first2 != __last2)
+             _M_transfer(__last1, __first2, __last2);
 
-         this->_M_inc_size(__x._M_get_size());
-         __x._M_set_size(0);
+           this->_M_inc_size(__x._M_get_size());
+           __x._M_set_size(0);
+         }
+         __catch(...)
+           {
+             size_t __dist = std::distance(__first2, __last2);
+             this->_M_inc_size(__dist);
+             __x._M_set_size(__orig_size - __dist);
+             __throw_exception_again;
+           }
        }
     }
 
@@ -423,20 +433,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
            iterator __last1 = end();
            iterator __first2 = __x.begin();
            iterator __last2 = __x.end();
-           while (__first1 != __last1 && __first2 != __last2)
-             if (__comp(*__first2, *__first1))
-               {
-                 iterator __next = __first2;
-                 _M_transfer(__first1, __first2, ++__next);
-                 __first2 = __next;
-               }
-             else
-               ++__first1;
-           if (__first2 != __last2)
-             _M_transfer(__last1, __first2, __last2);
-
-           this->_M_inc_size(__x._M_get_size());
-           __x._M_set_size(0);
+           const size_t __orig_size = __x.size();
+           __try
+             {
+               while (__first1 != __last1 && __first2 != __last2)
+                 if (__comp(*__first2, *__first1))
+                   {
+                     iterator __next = __first2;
+                     _M_transfer(__first1, __first2, ++__next);
+                     __first2 = __next;
+                   }
+                 else
+                   ++__first1;
+               if (__first2 != __last2)
+                 _M_transfer(__last1, __first2, __last2);
+
+               this->_M_inc_size(__x._M_get_size());
+               __x._M_set_size(0);
+             }
+           __catch(...)
+             {
+               size_t __dist = std::distance(__first2, __last2);
+               this->_M_inc_size(__dist);
+               __x._M_set_size(__orig_size - __dist);
+               __throw_exception_again;
+             }
          }
       }
 
@@ -453,27 +474,36 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
         list __tmp[64];
         list * __fill = __tmp;
         list * __counter;
-
-        do
+       __try
          {
-           __carry.splice(__carry.begin(), *this, begin());
-
-           for(__counter = __tmp;
-               __counter != __fill && !__counter->empty();
-               ++__counter)
+           do
              {
-               __counter->merge(__carry);
+               __carry.splice(__carry.begin(), *this, begin());
+
+               for(__counter = __tmp;
+                   __counter != __fill && !__counter->empty();
+                   ++__counter)
+                 {
+                   __counter->merge(__carry);
+                   __carry.swap(*__counter);
+                 }
                __carry.swap(*__counter);
+               if (__counter == __fill)
+                 ++__fill;
              }
-           __carry.swap(*__counter);
-           if (__counter == __fill)
-             ++__fill;
-         }
-       while ( !empty() );
+           while ( !empty() );
 
-        for (__counter = __tmp + 1; __counter != __fill; ++__counter)
-          __counter->merge(*(__counter - 1));
-        swap( *(__fill - 1) );
+           for (__counter = __tmp + 1; __counter != __fill; ++__counter)
+             __counter->merge(*(__counter - 1));
+           swap( *(__fill - 1) );
+         }
+       __catch(...)
+         {
+           this->splice(this->end(), __carry);
+           for (int i = 0; i < sizeof(__tmp)/sizeof(__tmp[0]); ++i)
+             this->splice(this->end(), __tmp[i]);
+           __throw_exception_again;
+         }
       }
     }
 
@@ -530,27 +560,36 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
            list __tmp[64];
            list * __fill = __tmp;
            list * __counter;
-
-           do
+           __try
              {
-               __carry.splice(__carry.begin(), *this, begin());
-
-               for(__counter = __tmp;
-                   __counter != __fill && !__counter->empty();
-                   ++__counter)
+               do
                  {
-                   __counter->merge(__carry, __comp);
+                   __carry.splice(__carry.begin(), *this, begin());
+
+                   for(__counter = __tmp;
+                       __counter != __fill && !__counter->empty();
+                       ++__counter)
+                     {
+                       __counter->merge(__carry, __comp);
+                       __carry.swap(*__counter);
+                     }
                    __carry.swap(*__counter);
+                   if (__counter == __fill)
+                     ++__fill;
                  }
-               __carry.swap(*__counter);
-               if (__counter == __fill)
-                 ++__fill;
-             }
-           while ( !empty() );
+               while ( !empty() );
 
-           for (__counter = __tmp + 1; __counter != __fill; ++__counter)
-             __counter->merge(*(__counter - 1), __comp);
-           swap(*(__fill - 1));
+               for (__counter = __tmp + 1; __counter != __fill; ++__counter)
+                 __counter->merge(*(__counter - 1), __comp);
+               swap(*(__fill - 1));
+             }
+           __catch(...)
+             {
+               this->splice(this->end(), __carry);
+               for (int i = 0; i < sizeof(__tmp)/sizeof(__tmp[0]); ++i)
+                 this->splice(this->end(), __tmp[i]);
+               __throw_exception_again;
+             }
          }
       }
 
diff --git a/libstdc++-v3/testsuite/23_containers/list/operations/78389.cc b/libstdc++-v3/testsuite/23_containers/list/operations/78389.cc
new file mode 100644 (file)
index 0000000..1cf9b0c
--- /dev/null
@@ -0,0 +1,85 @@
+// { dg-do run { target c++11 } }
+
+// Copyright (C) 2017 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/>.
+
+// 23.2.2.4 list operations [lib.list.ops]
+
+#include <testsuite_hooks.h>
+
+#include <list>
+
+struct ThrowingComparator
+{
+  unsigned int throw_after = 0;
+  unsigned int count = 0;
+  bool operator()(int, int) {
+    if (++count >= throw_after) {
+      throw 666;
+    }
+    return true;
+  }
+};
+
+struct X
+{
+  X() = default;
+  X(int) {}
+};
+
+unsigned int throw_after_X = 0;
+unsigned int count_X = 0;
+
+bool operator<(const X&, const X&) {
+  if (++count_X >= throw_after_X) {
+    throw 666;
+  }
+  return true;
+}
+
+
+int main()
+{
+  std::list<int> a{1, 2, 3, 4};
+  std::list<int> b{5, 6, 7, 8, 9, 10, 11, 12};
+  try {
+    a.merge(b, ThrowingComparator{5});
+  } catch (...) {
+  }
+  VERIFY(a.size() == 8 && b.size() == 4);
+  std::list<X> ax{1, 2, 3, 4};
+  std::list<X> bx{5, 6, 7, 8, 9, 10, 11, 12};
+  throw_after_X = 5;
+  try {
+    ax.merge(bx);
+  } catch (...) {
+  }
+  VERIFY(ax.size() == 8 && bx.size() == 4);
+  std::list<int> ay{5, 6, 7, 8, 9, 10, 11, 12};
+  try {
+    ay.sort(ThrowingComparator{5});
+  } catch (...) {
+  }
+  VERIFY(ay.size() == 8);
+  std::list<X> az{5, 6, 7, 8, 9, 10, 11, 12};
+  throw_after_X = 5;
+  try {
+    az.sort();
+  } catch (...) {
+  }
+  VERIFY(az.size() == 8);
+}