Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to replace operator new/delete and not interfere with libraries?

Tags:

c++

linux

Let's say I want to add some meta information to allocated objects to track allocations/deallocations. I learned that it's enough to replace one version of operator new and two versions of operator delete to handle all allocations since C++11.

Here's what I've written:

#include <cstdlib>
#include <FreeImage.h>

#include <new>
#include <iostream>

void *operator new(size_t size)
{
    std::cout << "allocation of size " << size << '\n';
    void *allocated = malloc(size + sizeof(size_t));
    *reinterpret_cast<size_t *>(allocated) = size;
    return reinterpret_cast<void *>(reinterpret_cast<size_t *>(allocated) + 1);
}

void _delete(void *ptr) {
    void *allocated = reinterpret_cast<void *>(reinterpret_cast<size_t *>(ptr) - 1);
    size_t size = *reinterpret_cast<size_t *>(allocated);
    std::cout << "deallocation of size " << size << '\n';
    free(allocated);
}

void operator delete(void *ptr) noexcept
{
    _delete(ptr);
}

void operator delete(void *ptr, std::align_val_t al) noexcept
{
    _delete(ptr);
}

int main()
{
    auto str = new char[1337];
    delete str;
    FreeImage_Initialise();
}

// compiled with `g++ -std=c++17 reproduce.cpp -lfreeimage`

This program works if I don't use any third-party functions, but fails if I do.

The program crash with free(): invalid pointer and gdb says it fails inside of _delete which, I guess, means that something was allocated with standard allocator, but was freed with my own.

GDB output:

(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007ffff79a9535 in __GI_abort () at abort.c:79
#2  0x00007ffff7a10516 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ffff7b34c00 "%s\n") at ../sysdeps/posix/libc_fatal.c:181
#3  0x00007ffff7a173aa in malloc_printerr (str=str@entry=0x7ffff7b32d85 "free(): invalid pointer") at malloc.c:5336
#4  0x00007ffff7a191fc in _int_free (av=<optimized out>, p=<optimized out>, have_lock=<optimized out>) at malloc.c:4143
#5  0x00005555555552e8 in _delete(void*) ()
#6  0x0000555555555303 in operator delete(void*) ()
#7  0x00007ffff7d7edc2 in ?? () from /usr/lib/x86_64-linux-gnu/libfreeimage.so.3
#8  0x00007ffff79cba77 in __cxa_finalize (d=0x7ffff7fa0000) at cxa_finalize.c:83
#9  0x00007ffff7d17003 in ?? () from /usr/lib/x86_64-linux-gnu/libfreeimage.so.3
#10 0x00007fffffffdec0 in ?? ()
#11 0x00007ffff7fe3d16 in _dl_fini () at dl-fini.c:138

So the question: How to replace new/delete properly?

like image 662
Yegor Tyuvaev Avatar asked Jan 04 '19 18:01

Yegor Tyuvaev


1 Answers

I found the answer for my case.

I replaced these forms to have it working:

void *operator new(size_t size);

void *operator new(size_t size, std::align_val_t al);

void *operator new(size_t size, const std::nothrow_t &tag);

void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &);

void operator delete(void *ptr);

Though I didn't replace aligned delete operator, the program stopped crashing. But I guess it's still a good idea to replace aligned delete as well as cppreference.com suggests.

like image 162
Yegor Tyuvaev Avatar answered Sep 28 '22 04:09

Yegor Tyuvaev