<html>
<head>
<base href="http://llvm.org/bugs/" />
</head>
<body><table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Bug ID</th>
<td><a class="bz_bug_link
bz_status_NEW "
title="NEW --- - std::deque cannot tolerate allocator exceptions"
href="http://llvm.org/bugs/show_bug.cgi?id=22650">22650</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>std::deque cannot tolerate allocator exceptions
</td>
</tr>
<tr>
<th>Product</th>
<td>libc++
</td>
</tr>
<tr>
<th>Version</th>
<td>3.5
</td>
</tr>
<tr>
<th>Hardware</th>
<td>PC
</td>
</tr>
<tr>
<th>OS</th>
<td>Linux
</td>
</tr>
<tr>
<th>Status</th>
<td>NEW
</td>
</tr>
<tr>
<th>Severity</th>
<td>normal
</td>
</tr>
<tr>
<th>Priority</th>
<td>P
</td>
</tr>
<tr>
<th>Component</th>
<td>All Bugs
</td>
</tr>
<tr>
<th>Assignee</th>
<td>unassignedclangbugs@nondot.org
</td>
</tr>
<tr>
<th>Reporter</th>
<td>tavianator@tavianator.com
</td>
</tr>
<tr>
<th>CC</th>
<td>llvmbugs@cs.uiuc.edu, mclow.lists@gmail.com
</td>
</tr>
<tr>
<th>Classification</th>
<td>Unclassified
</td>
</tr></table>
<p>
<div>
<pre>Created <span class=""><a href="attachment.cgi?id=13919" name="attach_13919" title="Reproducer">attachment 13919</a> <a href="attachment.cgi?id=13919&action=edit" title="Reproducer">[details]</a></span>
Reproducer
In std::deque::{push,emplace}_{back,front}, if the allocator/operator new
throws std::bad_alloc, bad things happen:
$ clang++ -std=c++11 -stdlib=libc++ -lc++abi -DOPERATION=push_back
deque_bug.cpp && ./a.out
[2] 22243 segmentation fault (core dumped) ./a.out
$ clang++ -std=c++11 -stdlib=libc++ -lc++abi -DOPERATION=push_front
deque_bug.cpp && ./a.out
*** Error in `./a.out': free(): invalid pointer: 0x00007f8bf263ccd8 ***
...
The problem seems to be at lines 2281 and 2429 of <deque> which do
try
{
__buf.push_back(__alloc_traits::allocate(__a, __base::__block_size));
}
catch (...)
{
__alloc_traits::deallocate(__a, __buf.{front,back}(),
__base::__block_size);
throw;
}
This code seems wrong for a couple reasons:
- If allocate() throws, __buf.push_back() is never called, so
__buf.{front,back}() is bogus
- if __buf.push_back() throws, __buf.{front,back}() is *still* bogus
The following patch fixes my test case. There are other cases that should
probably be fixed as well, everywhere "catch (...)" appears, but I couldn't
trigger them so I didn't try to fix them. Actually, assuming the capacity of
__buf is set correctly, I imagine the try/catch blocks could be removed
entirely, but I didn't test that.
--- /usr/include/c++/v1/deque 2014-03-05 14:06:20.000000000 -0500
+++ deque 2015-02-20 16:15:06.500667124 -0500
@@ -2269,16 +2269,17 @@
__split_buffer<pointer, typename __base::__pointer_allocator&>
__buf(max<size_type>(2 * __base::__map_.capacity(), 1),
0, __base::__map_.__alloc());
+ pointer __block = __alloc_traits::allocate(__a, __base::__block_size);
#ifndef _LIBCPP_NO_EXCEPTIONS
try
{
#endif // _LIBCPP_NO_EXCEPTIONS
- __buf.push_back(__alloc_traits::allocate(__a,
__base::__block_size));
+ __buf.push_back(__block);
#ifndef _LIBCPP_NO_EXCEPTIONS
}
catch (...)
{
- __alloc_traits::deallocate(__a, __buf.front(),
__base::__block_size);
+ __alloc_traits::deallocate(__a, __block, __base::__block_size);
throw;
}
#endif // _LIBCPP_NO_EXCEPTIONS
@@ -2417,16 +2418,17 @@
__buf(max<size_type>(2* __base::__map_.capacity(), 1),
__base::__map_.size(),
__base::__map_.__alloc());
+ pointer __block = __alloc_traits::allocate(__a, __base::__block_size);
#ifndef _LIBCPP_NO_EXCEPTIONS
try
{
#endif // _LIBCPP_NO_EXCEPTIONS
- __buf.push_back(__alloc_traits::allocate(__a,
__base::__block_size));
+ __buf.push_back(__block);
#ifndef _LIBCPP_NO_EXCEPTIONS
}
catch (...)
{
- __alloc_traits::deallocate(__a, __buf.back(),
__base::__block_size);
+ __alloc_traits::deallocate(__a, __block, __base::__block_size);
throw;
}
#endif // _LIBCPP_NO_EXCEPTIONS</pre>
</div>
</p>
<hr>
<span>You are receiving this mail because:</span>
<ul>
<li>You are on the CC list for the bug.</li>
</ul>
</body>
</html>