From e5dcfacf4390abe657d186eff582835e8d1b8370 Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Fri, 13 Jan 2017 16:46:25 +0200 Subject: [PATCH] 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 --- libstdc++-v3/ChangeLog | 11 ++ libstdc++-v3/include/bits/list.tcc | 159 +++++++++++------- .../23_containers/list/operations/78389.cc | 85 ++++++++++ 3 files changed, 195 insertions(+), 60 deletions(-) create mode 100644 libstdc++-v3/testsuite/23_containers/list/operations/78389.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 55fb708de1b..76d86fbf705 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,14 @@ +2017-01-13 Ville Voutilainen + + 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 * 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 c4f397feea9..5be49a83997 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 00000000000..1cf9b0c58d1 --- /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 +// . + +// 23.2.2.4 list operations [lib.list.ops] + +#include + +#include + +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 a{1, 2, 3, 4}; + std::list 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 ax{1, 2, 3, 4}; + std::list 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 ay{5, 6, 7, 8, 9, 10, 11, 12}; + try { + ay.sort(ThrowingComparator{5}); + } catch (...) { + } + VERIFY(ay.size() == 8); + std::list az{5, 6, 7, 8, 9, 10, 11, 12}; + throw_after_X = 5; + try { + az.sort(); + } catch (...) { + } + VERIFY(az.size() == 8); +} -- 2.30.2