aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVille Voutilainen <ville.voutilainen@gmail.com>2017-01-13 16:46:25 +0200
committerVille Voutilainen <ville@gcc.gnu.org>2017-01-13 16:46:25 +0200
commite5dcfacf4390abe657d186eff582835e8d1b8370 (patch)
tree0b93988d916d1c7b514a4937985f463ef43f4cb6
parent45cac8baeb358b76c7255841543190cf3eeff2be (diff)
downloadgcc-e5dcfacf4390abe657d186eff582835e8d1b8370.zip
gcc-e5dcfacf4390abe657d186eff582835e8d1b8370.tar.gz
gcc-e5dcfacf4390abe657d186eff582835e8d1b8370.tar.bz2
re PR libstdc++/78389 (list::merge and list::sort are not exception safe)
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
-rw-r--r--libstdc++-v3/ChangeLog11
-rw-r--r--libstdc++-v3/include/bits/list.tcc159
-rw-r--r--libstdc++-v3/testsuite/23_containers/list/operations/78389.cc85
3 files changed, 195 insertions, 60 deletions
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index 55fb708..76d86fb 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -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
diff --git a/libstdc++-v3/include/bits/list.tcc b/libstdc++-v3/include/bits/list.tcc
index c4f397f..5be49a8 100644
--- a/libstdc++-v3/include/bits/list.tcc
+++ b/libstdc++-v3/include/bits/list.tcc
@@ -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
index 0000000..1cf9b0c
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/list/operations/78389.cc
@@ -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);
+}