mavam
mavam

Reputation: 12572

Issue with stack allocator with embedded arena

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

Answers (2)

TemplateRex
TemplateRex

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

Kerrek SB
Kerrek SB

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 of a.

Two allocators are "equal" if one can deallocate memory allocated by the other, which is blatantly not the case here.

Upvotes: 2

Related Questions