Reputation: 12572
I'm experiencing crashes when using Howard Hinnant's stack-based allocator, both on MacOS with and on 64-bit Linux with Clang 3.4. Here is a minimal example triggering the crash in the destructor of the container:
#include <vector>
#include "stack_alloc.h"
template <template <typename...> class C, typename T, size_t N>
using stack_container = C<T, stack_alloc<T, N>>;
using stack_vector = stack_container<std::vector, size_t, 4>;
int main()
{
auto s = stack_vector{1, 2, 3};
auto m = std::move(s);
}
Compiled as follows:
clang++ -std=c++11 -stdlib=libc++ -g -Wall crash.cc && ./a.out
Do you have any ideas why this crash would happen? I've also tried
reimplemented stack_alloc
in terms of the arena implementation from
short_alloc, but I still get the crash when moving a stack-based container.
Here is a Linux backtrace:
#0 _int_free (av=0x394f5b8760 <main_arena>, p=0x7fffffffe0f0, have_lock=0) at malloc.c:3901
#1 0x00000000004013eb in stack_alloc<unsigned long, 4ul>::deallocate (this=0x7fffffffe080, p=0x7fffffffe100, n=3)
at ./stack_alloc.h:71
#2 0x0000000000401343 in capacity (this=0x7fffffffe060, this=0x7fffffffe060, __a=..., __p=0x7fffffffe100, __n=3)
at .../include/c++/v1/memory:1443
#3 std::__1::__vector_base<unsigned long, stack_alloc<unsigned long, 4> >::~__vector_base (this=0x7fffffffe060)
at .../include/c++/v1/vector:476
#4 0x0000000000400fa5 in std::__1::vector<unsigned long, stack_alloc<unsigned long, 4> >::~vector (this=0x7fffffffe060)
at .../include/c++/v1/vector:481
#5 0x0000000000400f6e in main () at crash.cc:13
I'd be interested (i) if folks can reproduce the error, and (ii) how to fix it.
Upvotes: 2
Views: 351
Reputation: 70556
You are using the non-conforming C++11 stack_alloc
. As Hinnant himself wrote,
I've updated this article with a new allocator that is fully C++11 conforming. The allocator it replaces was not fully C++03 nor C++11 conforming because copies were not equal.
The corrected version is named short_alloc
and is found here.
Usage requires placing the stack buffer outside the allocator (using Hinnant's notation):
int main()
{
arena<N> a; // change N to required space
auto s = SmallVector({1, 2, 3}, Alloc{a});
auto m = std::move(s);
}
Upvotes: 4
Reputation: 477444
When you move-construct the second container, the container move-constructs the allocator and takes over the pointers. When the second container dies, the it tries to deallocate the pointers and does it wrongly, causing UB in the pointer comparison in deallocate()
.
The "allocator" does not satisfy the allocator requirements, particularly:
X a1(move(a))
Post:a1
equals the prior value ofa
.
Two allocators are "equal" if one can deallocate memory allocated by the other, which is blatantly not the case here.
Upvotes: 2