https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98798

--- Comment #4 from Jonathan Wakely <redi at gcc dot gnu.org> ---
#include <memory>

using std::size_t;

struct alignas(32) Foo
{
    char x;

    void * operator new[ ] (size_t s, std::align_val_t a)
    {
        void* p =  aligned_alloc(static_cast<size_t>(a), s);
        __builtin_printf ("p: %p s: %zu, a: %zu\n", p, s, (size_t)a);
        return p;
    }

    void operator delete[ ] (void *p, size_t s, std::align_val_t a)
    {
        __builtin_printf ("p: %p s: %zu, a: %zu\n", p, s, (size_t)a);
        free(p);
    }
};

int main()
{
    auto p = std::make_unique<Foo[]>(3);
    __builtin_printf("p: %p\n", p.get());
}

For this code clang prints:

p: 0x4daacc0 s: 128, a: 32
p: 0x4daace0
p: 0x4daacc0 s: 128, a: 32

Notice that the p.get() pointer is not equal to the value returned by
aligned_alloc, because the compiler adds a cookie and then adjusts the pointer.

G++ prints:

p: 0x4dd2cc0 s: 96, a: 32
p: 0x4dd2cc0
==1233795== Invalid read of size 8
==1233795==    at 0x40144D: std::enable_if<std::is_convertible<Foo (*) [], Foo
(*) []>::value, void>::type std::default_delete<Foo []>::operator()<Foo>(Foo*)
const (unique_ptr.h:120)
==1233795==    by 0x401328: std::unique_ptr<Foo [], std::default_delete<Foo []>
>::~unique_ptr() (unique_ptr.h:612)
==1233795==    by 0x4011B9: main (98798.C:25)
==1233795==  Address 0x4dd2cb8 is 8 bytes before a block of size 96 alloc'd
==1233795==    at 0x483D01C: memalign (vg_replace_malloc.c:907)
==1233795==    by 0x401203: Foo::operator new[](unsigned long,
std::align_val_t) (98798.C:11)
==1233795==    by 0x4012A4: std::_MakeUniq<Foo []>::__array
std::make_unique<Foo []>(unsigned long) (unique_ptr.h:968)
==1233795==    by 0x40118F: main (98798.C:25)
==1233795== 
p: 0x4dd2cc0 s: 0, a: 32

There are three problems here.

Firstly, the p.get() pointer is the same as that returned by aligned_alloc,
meaning there is no array cookie.

Secondly, the delete[] expression at unique_ptr.h:120 expects a cookie and so
inspects the 8 bytes before the pointer value (which valgrind correctly
diagnoses as invalid).

Thirdly, the size passed to Foo::operator delete[] is zero. Probably because
the 8 bytes before the allocation (where the cookie should be) happen to be
zero.

This is a G++ bug.

Reply via email to