From: Ville Voutilainen Date: Sun, 15 Jan 2017 16:27:08 +0000 (+0200) Subject: backport: re PR libstdc++/78389 (list::merge and list::sort are not exception safe) X-Git-Tag: releases/gcc-5.5.0~585 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4322dc13283f37ab02c2321566150df84667f022;p=thirdparty%2Fgcc.git backport: re PR libstdc++/78389 (list::merge and list::sort are not exception safe) Backport from mainline 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. * testsuite/23_containers/list/operations/78389.cc: New. From-SVN: r244475 --- diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 7f9550b958df..2a3c4d53c8e4 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,14 @@ +2017-01-15 Ville Voutilainen + + Backport from mainline + 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. + * testsuite/23_containers/list/operations/78389.cc: New. + 2017-01-09 Jonathan Wakely * testsuite/30_threads/thread/cons/lwg2097.cc: Compile with diff --git a/libstdc++-v3/include/bits/list.tcc b/libstdc++-v3/include/bits/list.tcc index a9c8a550b4ac..22a5530aa5d6 100644 --- a/libstdc++-v3/include/bits/list.tcc +++ b/libstdc++-v3/include/bits/list.tcc @@ -338,26 +338,36 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER // 300. list::merge() specification incomplete if (this != &__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); + 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 = distance(__first2, __last2); + this->_M_inc_size(__dist); + __x._M_set_size(__orig_size - __dist); + __throw_exception_again; + } } } @@ -381,20 +391,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); + 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 = distance(__first2, __last2); + this->_M_inc_size(__dist); + __x._M_set_size(__orig_size - __dist); + __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 000000000000..3149a1f8c62a --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/list/operations/78389.cc @@ -0,0 +1,71 @@ +// { dg-options "-std=gnu++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; + unsigned int count; + 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, 0}); + } 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 (...) { + } +}