проблема выравнивания памяти при замене calloc()?

Используя приведенный ниже код, я пытаюсь написать оболочку для calloc(), чтобы я мог отслеживать выделенную память кучи, сохраняя размер в 1-х 2/4 байтах выделенной памяти. Когда я тестировал только это, кажется, все в порядке. Но когда я заменяю это как свою систему calloc(), то это создает проблемы.. иногда это означает, что она возвращает NULL, даже если доступно много кучи.

Я запускаю это на плате ARM с помощью компилятора IAR:

void *MyCalloc(size_t size) {
    size_t new_size = ((size + 3) & ~0x3); 
    size_t *result = calloc(1,new_size + sizeof(size_t)); 
    if ( result ) { 
        printf("MyCalloc addr: %p\n", result);
        *result = (new_size + sizeof(size_t));
        result = result + sizeof(size_t);
    } 
return result;
}

Любая идея, почему это вызывает проблему?


person ashok449    schedule 16.02.2013    source источник
comment
Это ошибка компилятора? - Нет, это твой код.   -  person    schedule 16.02.2013
comment
Как вы заменяете calloc ?   -  person cnicutar    schedule 16.02.2013
comment
Последняя строка должна быть: результат = результат + 1?   -  person Luka Rahne    schedule 16.02.2013
comment
@LukaRahne да, так и должно быть.   -  person John Szakmeister    schedule 16.02.2013
comment
@ H2CO3 Это не очень полезно.   -  person John Szakmeister    schedule 16.02.2013


Ответы (2)


Есть несколько проблем с вашим кодом:

  1. При написании функций кучи или оберток кучи просто не используйте арифметику указателей для хранения заголовков кучи. Используйте структуры. Вот для чего они.

  2. Вы добавили в свой код ряд ошибок целочисленного переполнения. Если кто-то запросит у вашего calloc() байты 0xfffffffe, вы вернете им 4 байта. Если они запишут в это выделение более 4 байтов, произойдет переполнение кучи.

  3. Ваш calloc() не имеет той же подписи, что и calloc(). В зависимости от того, как вы меняете calloc(), это может стать проблемой.

  4. calloc() и malloc() естественным образом возвращают выровненные указатели. В x86 им нужно вернуть указатель на приложение, выровненный не менее чем по 8 байтам, а в x64 им нужно вернуть указатель, выровненный не менее чем по 16 байтам.

    В вашем коде вы используете настоящий calloc для выполнения тяжелой работы (т. е. в качестве необработанного распределителя), что нормально и возвращает 8- или 16-байтовый выровненный указатель, но когда вы возвращаете указатель 4 байта в этой структуры ваш calloc в конечном итоге возвращает невыровненный указатель вызывающему объекту, что, вероятно, вызовет проблемы у людей, вызывающих замену calloc.

Попробуйте немного больше кода:

 typedef struct 
 {
    size_t cbSize;
 } MyAwesomeHeapHeader;
 // TODO: ensure that MyAwesomeHeapHeader is 8-byte aligned on x86 and 16-byte aligned on x64 (or just 16-byte aligned on both).

 void* MyAwesomeMalloc(size_t cbSize)
 {
    MyAwesomeHeapHeader* header;
    void* internalAllocatorPtr;
    size_t cbAlloc;
    // TODO: Maybe I want a heap footer as well?

    // TODO: I should really check the following for an integer overflow:
    cbAlloc = sizeof(MyAwesomeHeapHeader) + cbSize; 
    internalAllocatorPtr = MyAwesomeRawAllocator(cbAlloc); // at the moment you're using the real calloc for this, but you could use malloc or write your own raw allocator
    // TODO: Check for null

    header = (MyAwesomeHeapHeader*)internalAllocatorPtr;
    header->heapSize = cbSize;
    // TODO: other fields here.

    return (uint8_t*)(internalAllocatorPtr) + sizeof(MyAwesomeHeapHeader);
 }
person SecurityMatt    schedule 16.02.2013

Ваша проблема в этой строке:

result = result + sizeof(size_t);

При добавлении указателя число неявно умножается на размер указателя. Так

result = result + 1;

переместит указатель результата вперед на sizeof(size_t) байта. Это то, что вы хотите. Потому что

result = result + sizeof(size_t);

перемещает указатель результата вперед на sizeof(size_t) * sizeof(size_t) байт.

Из-за этого указатель result не указывает на правильное местоположение, вызывающий код переполнит конец выделенного буфера, повредив кучу.

person shf301    schedule 16.02.2013
comment
Я пробовал с *result++ = (new_size + sizeof(size_t)); а затем вернуть результат; все еще не работает. для меня, когда значения равны 16 байтам, он работает за один раз, и немедленный запрос 16 байтов возвращает NULL - person ashok449; 16.02.2013