1

使用下面的代码,我正在尝试编写一个包装器,calloc()以便我可以通过将大小存储在分配内存的第一个 2/4 字节中来跟踪分配的堆内存。当我单独测试时,它似乎没问题。但是,当我将它替换为我的系统时calloc(),它会产生问题......有时意味着它会返回NULL,即使有很多可用的堆。

我正在使用 IAR 编译器在 ARM 板上运行它:

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;
}

知道为什么这会导致问题吗?

4

2 回答 2

5

您的代码有几个问题:

  1. 在编写堆函数或堆包装器时,不要使用指针算法来存储堆头。使用结构。这就是他们的目的。

  2. 您在代码中引入了许多整数溢出错误。如果有人向您询问calloc()0xffffffffe 字节,您将返回 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);
 }
于 2013-02-16T18:59:16.963 回答
5

你的问题是这一行:

result = result + sizeof(size_t);

通过指针加法,数字隐式乘以指针的大小。所以

result = result + 1;

将结果指针向前移动sizeof(size_t)字节。这就是你想要的。因为

result = result + sizeof(size_t);

将结果指针向前移动sizeof(size_t) * sizeof(size_t)字节。

因此,result指针没有指向正确的位置,调用代码将溢出分配的缓冲区的末尾,从而破坏堆。

于 2013-02-16T18:36:56.430 回答