проблем с подравняването на паметта при замяната на 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
Последният ред трябва да бъде restult = result + 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