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" 并发现了一些需要几分钟检查才能理解的东西。这是非常棒的代码。很多非常聪明的人花了很长时间研究它。
我有以下代码:
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 betesttest
and the rest would overflow intoanotherStr
为了使 argsStr
字符串在打印 8 个字符后停止,第 9 个字符必须是 '[=14=]'
。您的字符串没有它,因此 printf
不知道在 %s
.
您遇到了堆溢出,因为 strcpy
超出了分配的大小。它还炸毁了 malloc
存储的 "bookkeeping info",并溢出到下一个分配中。当然它不必进入下一个分配的块,因为它是未定义的行为;它恰好在您的特定系统上执行此操作。
您可以通过 运行 您的程序通过 valgrind 判断存在堆溢出。当您添加对 free
您分配的内存的调用时,您的程序很可能会崩溃。
如您所知,当您执行 malloc 时,它会为您提供一个指向堆中内存块的指针。堆的确切结构取决于实现。正如其他人所观察到的,您可能会或可能不会获得顺序内存,并且您可能会或可能不会获得您要求的内存量。有一些调试实现 malloc 可以为您提供所需的内存,并在末尾加上一个大区域,并带有标记,以便在您覆盖已分配块的末尾时进行查找。还有一点要记住,第二个 malloc 可以在第一个 malloc 的内存之前或之后。就像 MM 观察到的那样,你可以做一个 %p 来查看块的位置,以了解它们是否可以 运行 进入彼此。
这就是所谓的未定义行为。探索未定义的行为非常有趣,可以让您深入了解实现,但如果它随不同的版本发生变化也不要感到惊讶。你几乎可以保证它会在不同的系统上发生重大变化,即使使用相同的 (GCC) 编译器也是如此。
如果您真的有兴趣,可以在网上找到 GCC 运行 时间库 malloc 例程的源代码,看看里面发生了什么。我刚刚搜索了 "gcc runtime library malloc source" 并发现了一些需要几分钟检查才能理解的东西。这是非常棒的代码。很多非常聪明的人花了很长时间研究它。