Valgrind неверная запись при каждом вызове realloc - PullRequest
2 голосов
/ 11 июня 2019

Я пытаюсь создать масштабируемый распределитель пула (пока только для встроенных типов данных).И когда я сканирую его с помощью valgrind, я получаю предупреждение:

Недопустимая запись размером 8

Программа, которую она самостоятельно компилирует и работает правильно

Код распределителя пула :

#include "pool_allocator.h"
//Pool allocator reference: http://www.thinkmind.org/download.php?articleid=computation_tools_2012_1_10_80006

namespace sc2d::memory {

void pool_allocator::create(size_t block_size, size_t blocks_numb, size_t alignment)
{
    size_of_block = block_size;
    num_of_blocks = blocks_numb;
    p_start = reinterpret_cast<unsigned char*>(aligned_malloc(block_size * blocks_numb, alignment));
    num_of_free_blocks = num_of_blocks;
    p_next = p_start;
}

void pool_allocator::destroy()
{
    aligned_free(p_start);
    p_start = nullptr;
}

void* pool_allocator::allocate()
{
    if(num_of_initialized < num_of_blocks)
    {
        size_t* ptr = (size_t*)addr_from_index(num_of_initialized);
        *ptr = num_of_initialized + 1;
        num_of_initialized++;
    }

    void* ptr = nullptr;
    if(num_of_free_blocks > 0)
    {
        ptr = (void*)p_next;
        --num_of_free_blocks;
        if(num_of_free_blocks != 0)
        {
            p_next = addr_from_index(*(size_t*)p_next);
        }
    }
    else
    {
        resize(num_of_blocks << 1);
        ptr = addr_from_index(num_of_initialized);
        num_of_initialized++;
        num_of_free_blocks--;
        p_next = addr_from_index(num_of_initialized);
    }

    return ptr;
}

void pool_allocator::resize(size_t new_size)
{
    num_of_free_blocks = num_of_blocks;
    num_of_blocks = new_size;
    if(void* p_new_start = realloc(p_start, size_of_block * num_of_blocks))
        p_start = reinterpret_cast<unsigned char*>(p_new_start);
    else
    {
        // TODO: Error handling
    }
}

void pool_allocator::deallocate(void* ptr)
{
    if(p_next != nullptr)
    {
        (*(size_t*)ptr) = index_from_addr(p_next);
        p_next = (unsigned char*)ptr;
    }
    else
    {
        (*(size_t*)ptr) = num_of_blocks;
        p_next = (unsigned char*)ptr;
    }
    num_of_free_blocks++;
    num_of_initialized--;
}

unsigned char* pool_allocator::addr_from_index(size_t index) const
{
    return p_start + index * size_of_block;
}

size_t pool_allocator::index_from_addr(const unsigned char* ptr) const
{
    return ((size_t)(ptr - p_start) / size_of_block);
}
}

Использование (код для тестирования) :

double *d_p1 = nullptr;
double *d_p2 = nullptr;
double *d_p3 = nullptr;
double *d_p4 = nullptr;
double *d_p5 = nullptr;
double *d_p6 = nullptr;
double *d_p7 = nullptr;
double *d_p8 = nullptr;
double *d_p9 = nullptr;

using namespace sc2d::memory;
std::unique_ptr<pool_allocator> p_alloc = std::make_unique<pool_allocator>();

p_alloc->create(sizeof(double), 2, alignof(double*));

d_p1 = (double*)p_alloc->allocate();
d_p2 = (double*)p_alloc->allocate();
d_p3 = (double*)p_alloc->allocate();
d_p4 = (double*)p_alloc->allocate();
d_p5 = (double*)p_alloc->allocate();
d_p6 = (double*)p_alloc->allocate();
d_p7 = (double*)p_alloc->allocate();
d_p8 = (double*)p_alloc->allocate();
d_p9 = (double*)p_alloc->allocate();


*d_p1 = 1.99;
*d_p2 = 2.99;
*d_p3 = 3.99;
*d_p4 = 4.99;
*d_p5 = 5.99;
*d_p6 = 6.99;
*d_p7 = 7.99;
*d_p8 = 8.99;
*d_p9 = 9.99;

Журнал Valgrind :

Invalid write of size 8
       at 0x10F45A: main (main.cpp:142)
Address 0x71b9e40 is 0 bytes inside a block of size 16 free'd
       at 0x4C31D2F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
       by 0x151EF7: sc2d::memory::pool_allocator::resize(unsigned long) (pool_allocator.cpp:62)
       by 0x151E3C: sc2d::memory::pool_allocator::allocate() (pool_allocator.cpp:48)
       by 0x10F3B9: main (main.cpp:133)
Block was alloc'd at
       at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
       by 0x151C6D: sc2d::memory::aligned_malloc(unsigned long, unsigned long) (aligned_allocator.cpp:22)
       by 0x151CFA: sc2d::memory::pool_allocator::create(unsigned long, unsigned long, unsigned long) (pool_allocator.cpp:16)
       by 0x10F375: main (main.cpp:129)

Invalid write of size 8
       at 0x10F469: main (main.cpp:143)
Address 0x71b9e48 is 8 bytes inside a block of size 16 free'd
       at 0x4C31D2F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
       by 0x151EF7: sc2d::memory::pool_allocator::resize(unsigned long) (pool_allocator.cpp:62)
       by 0x151E3C: sc2d::memory::pool_allocator::allocate() (pool_allocator.cpp:48)
       by 0x10F3B9: main (main.cpp:133)
Block was alloc'd at
       at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
       by 0x151C6D: sc2d::memory::aligned_malloc(unsigned long, unsigned long) (aligned_allocator.cpp:22)
       by 0x151CFA: sc2d::memory::pool_allocator::create(unsigned long, unsigned long, unsigned long) (pool_allocator.cpp:16)
       by 0x10F375: main (main.cpp:129)

Invalid write of size 8
      at 0x10F478: main (main.cpp:144)
Address 0x71b9ea0 is 16 bytes inside a block of size 32 free'd
      at 0x4C31D2F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
      by 0x151EF7: sc2d::memory::pool_allocator::resize(unsigned long) (pool_allocator.cpp:62)
      by 0x151E3C: sc2d::memory::pool_allocator::allocate() (pool_allocator.cpp:48)
      by 0x10F3E9: main (main.cpp:135)
Block was alloc'd at
      at 0x4C31D2F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
      by 0x151EF7: sc2d::memory::pool_allocator::resize(unsigned long) (pool_allocator.cpp:62)
      by 0x151E3C: sc2d::memory::pool_allocator::allocate() (pool_allocator.cpp:48)
      by 0x10F3B9: main (main.cpp:133)

......

HEAP SUMMARY:
in use at exit: 0 bytes in 0 blocks
total heap usage: 9 allocs, 9 frees, 73,112 bytes allocated
All heap blocks were freed -- no leaks are possible 
For counts of detected and suppressed errors, rerun with: -v
ERROR SUMMARY: 8 errors from 8 contexts (suppressed: 0 from 0)

Я знаю, что есть некоторые проблемы с этой реализацией распределителя пула:

  • reallocможет вернуть указатель на новую ячейку памяти (новый запуск).
  • Я использую выравнивающий распределитель, но realloc не гарантирует правильное выравнивание после перераспределения, поэтому, возможно, мне нужно решение , описанное здесь .

Спасибо!

UPD: ссылка на проводник Godbolt

UPD 2: Выровненный код malloc :

#include <memory>

#if defined(__GLIBC__) && ((__GLIBC__>=2 && __GLIBC_MINOR__>=8) || 
 __GLIBC__>2) \
 && defined(__LP64__)
 #define GLIBC_MALLOC_ALREADY_ALIGNED 1
#else
  #define GLIBC_MALLOC_ALREADY_ALIGNED 0
#endif

#if defined(__FreeBSD__) && !defined(__arm__) && !defined(__mips__)
  #define FREEBSD_MALLOC_ALREADY_ALIGNED 1
#else
  #define FREEBSD_MALLOC_ALREADY_ALIGNED 0
#endif

#if (defined(__APPLE__) \
  || defined(_WIN64) \
  || GLIBC_MALLOC_ALREADY_ALIGNED \
  || FREEBSD_MALLOC_ALREADY_ALIGNED)
#define MALLOC_ALREADY_ALIGNED 1
#else
 #define MALLOC_ALREADY_ALIGNED 0
#endif

#if ((defined __QNXNTO__) || (defined _GNU_SOURCE) || ((defined 
_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 600))) \
&& (defined _POSIX_ADVISORY_INFO) && (_POSIX_ADVISORY_INFO > 0)
    #define HAS_POSIX_MEMALIGN 1
#else
    #define HAS_POSIX_MEMALIGN 0
#endif
namespace sc2d::memory {

void* _aligned_malloc(size_t size, size_t alignment)
{
    void* res = nullptr;
    void* ptr = malloc(size + alignment);
    if (ptr!=nullptr) {
        res = reinterpret_cast<void*>((reinterpret_cast<size_t>(ptr) & ~(size_t(alignment-1))) + alignment);
        *(reinterpret_cast<void**>(res) - 1) = ptr;
    }
    return res;
}

void* aligned_malloc(size_t size, size_t alignment)
{
#if MALLOC_ALREADY_ALIGNED
    return malloc(size);
#elif HAS_POSIX_MEMALIGN
    void* res;
    const int failed = posix_memalign(&res,size,alignment);
    if(failed) res = 0;
    return res;
#elif (defined _MSC_VER)
    return _aligned_malloc(size, alignment);
#else
    return sc2d::memory::_aligned_malloc(size, alignment);
#endif
}

void _aligned_free(void* ptr)
{
    if (ptr != nullptr)
        free(*(reinterpret_cast<void**>(ptr) - 1));
}

void aligned_free(void* ptr)
{
#if MALLOC_ALREADY_ALIGNED
    free(ptr);
#elif HAS_POSIX_MEMALIGN
    free(ptr);
#elif defined(_MSC_VER)
    _aligned_free(ptr);
#else
    sc2d::memory_aligned_free(ptr);
#endif
}
}
...