Commit e5dcfacf by Ville Voutilainen Committed by Ville Voutilainen

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
parent 45cac8ba
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
......
......@@ -386,6 +386,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator __last1 = end();
iterator __first2 = __x.begin();
iterator __last2 = __x.end();
const size_t __orig_size = __x.size();
__try {
while (__first1 != __last1 && __first2 != __last2)
if (*__first2 < *__first1)
{
......@@ -401,6 +403,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
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;
}
}
}
template<typename _Tp, typename _Alloc>
......@@ -423,6 +433,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator __last1 = end();
iterator __first2 = __x.begin();
iterator __last2 = __x.end();
const size_t __orig_size = __x.size();
__try
{
while (__first1 != __last1 && __first2 != __last2)
if (__comp(*__first2, *__first1))
{
......@@ -438,6 +451,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
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;
}
}
}
template<typename _Tp, typename _Alloc>
......@@ -453,7 +474,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
list __tmp[64];
list * __fill = __tmp;
list * __counter;
__try
{
do
{
__carry.splice(__carry.begin(), *this, begin());
......@@ -475,6 +497,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
__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;
}
}
}
template<typename _Tp, typename _Alloc>
......@@ -530,7 +560,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
list __tmp[64];
list * __fill = __tmp;
list * __counter;
__try
{
do
{
__carry.splice(__carry.begin(), *this, begin());
......@@ -552,6 +583,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
__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;
}
}
}
_GLIBCXX_END_NAMESPACE_CONTAINER
......
// { 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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment