我有以下代码:
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'而另一个是'testtesttesttesttest'
这是为什么?我是否遗漏了堆溢出或malloc()
的内容?
答案 0 :(得分:7)
printf()
不知道或关心您为缓冲区分配了多少内存。当它打印一个%s
格式的字符串时,它会一直打印,直到达到终止零字节。因此,当它打印argsStr
时,它会打印整个内容,即使它溢出了已分配的8个字节。这就是为什么缓冲区溢出是一个问题--C指针不包含有关分配了多少内存的任何信息,因此如果你没有正确检查你的长度,你可以轻松访问分配空间之外的内存。 / p>
anotherStr
的内存显然是在argsStr
的内存之后分配了16个字节。因此,当您打印它时,它从argsStr[16]
的位置开始,并打印该字符串的最后20个字节。
当然,这是所有未定义的行为,因此您无法依赖任何特定结果。
答案 1 :(得分:2)
我希望,由于
anotherStr
的大小只有argsStr
,因此会'\0'
,其余的会溢出到printf
为了使%s
字符串在打印后8个字符后停止,第9个字符必须为strcpy
。你的字符串没有它,因此在malloc
打印前8个字符后,free
不知道要停止。
你的堆溢出了,因为$("#result").html("")
for( var key in data.query.pages){
$("#result").append(("<p>" + data.query.pages[key].title) + "</p>")
//etc..
}
超过了分配的大小。它还吹响了SET @SQLStmt1 = N'INSERT INTO TestTable( TableName )
VALUES(''' + @ResulTableName + ''')'
EXEC sp_executesql @SQLStmt1
存储的“簿记信息”,并溢出到下一个分配中。当然它不必进入下一个已分配的块,因为它是未定义的行为;它碰巧在你的特定系统上做到了。
您可以通过valgrind运行程序来判断堆存在溢出。有可能,当您向blockingButton
调用已分配的内存时,您的程序将崩溃。
答案 2 :(得分:2)
如您所知,当您执行malloc时,它会为您提供指向堆中内存块的指针。堆的确切结构取决于实现。正如其他人所观察到的那样,您可能会或可能不会获得顺序记忆,您可能会或可能不会得到您要求的内存量。有一些调试实现malloc可以为您提供所需的内存以及最后一个带有标记的大区域,以便在覆盖已分配块的末尾时查找。要记住的其他事情是第二个malloc可以在第一个malloc的内存之前或之后。像MM观察到的那样,你可以用%p来查看块的位置,以便了解它们是否可以相互碰撞。
这就是所谓的未定义行为。探索未定义的行为非常有趣,可以让您深入了解实施情况,但如果它的版本不同,请不要感到惊讶。几乎可以保证,即使使用相同的(GCC)编译器,它也会在不同的系统上发生重大变化。
如果您真的感兴趣,可以在网上找到GCC运行时库malloc例程的源代码,看看里面发生了什么。我刚刚搜索了&#34; gcc运行时库malloc源&#34;并找到了一些需要花费几分钟时间才能理解的东西。这是非常棒的代码。许多非常聪明的人花了很长时间来研究它。