malloc() 与堆溢出解释

malloc() with heap overflow explanation

我有以下代码:

int main(int argc, char *argv[]) {

    int bufferSize = 8;
    //Setting the buffer size here, which can cause a heap overflow
    char *argsStr = malloc(bufferSize);
    char *anotherStr = malloc(bufferSize);

    //If argv[1] is greater than the buffer size, we will have an overflow
    strcpy(argsStr, argv[1]);

    printf("String 1: %s String 2: %s", argsStr, anotherStr);

}

我想引起堆溢出,所以我导入了参数'testtesttesttesttesttesttesttesttest'。

我预计,由于 argsStr 的大小仅为 8,因此它将是 'testtest',其余部分将溢出到 anotherStr(8 个字节),但我看到的是:

所以 argsStr 是 'testtesttesttesttesttesttesttesttest' 而 anotherStr 是 'testtesttesttesttest'

这是为什么?我是否遗漏了堆溢出或 malloc() 的内容?

printf() 不知道也不关心您为缓冲区分配了多少内存。当它打印 %s 格式的字符串时,它会一直打印直到到达终止零字节。所以当它打印 argsStr 时,它会打印整个内容,即使它溢出了分配的 8 个字节。这就是缓冲区溢出是一个问题的原因——C 指针不包含任何有关分配了多少内存的信息,因此如果您没有正确检查长度,您可以轻松访问分配 space 之外的内存。

anotherStr 的内存显然是在 argsStr 的内存之后分配的 16 个字节。所以当你打印它时,它从 argsStr[16] 的位置开始,并打印该字符串的最后 20 个字节。

当然,这都是未定义的行为,因此您不能依赖任何特定结果。

I would expect, since argsStr is only of size 8, it would be testtest and the rest would overflow into anotherStr

为了使 argsStr 字符串在打印 8 个字符后停止,第 9 个字符必须是 '[=14=]'。您的字符串没有它,因此 printf 不知道在 %s.

上打印前 8 个字符后停止

您遇到了堆溢出,因为 strcpy 超出了分配的大小。它还炸毁了 malloc 存储的 "bookkeeping info",并溢出到下一个分配中。当然它不必进入下一个分配的块,因为它是未定义的行为;它恰好在您的特定系统上执行此操作。

您可以通过 运行 您的程序通过 valgrind 判断存在堆溢出。当您添加对 free 您分配的内存的调用时,您的程序很可能会崩溃。

如您所知,当您执行 malloc 时,它会为您提供一个指向堆中内存块的指针。堆的确切结构取决于实现。正如其他人所观察到的,您可能会或可能不会获得顺序内存,并且您可能会或可能不会获得您要求的内存量。有一些调试实现 malloc 可以为您提供所需的内存,并在末尾加上一个大区域,并带有标记,以便在您覆盖已分配块的末尾时进行查找。还有一点要记住,第二个 malloc 可以在第一个 malloc 的内存之前或之后。就像 MM 观察到的那样,你可以做一个 %p 来查看块的位置,以了解它们是否可以 运行 进入彼此。

这就是所谓的未定义行为。探索未定义的行为非常有趣,可以让您深入了解实现,但如果它随不同的版本发生变化也不要感到惊讶。你几乎可以保证它会在不同的系统上发生重大变化,即使使用相同的 (GCC) 编译器也是如此。

如果您真的有兴趣,可以在网上找到 GCC 运行 时间库 malloc 例程的源代码,看看里面发生了什么。我刚刚搜索了 "gcc runtime library malloc source" 并发现了一些需要几分钟检查才能理解的东西。这是非常棒的代码。很多非常聪明的人花了很长时间研究它。