我试图从较低的层次理解C如何管理内存。我在网页上找到了一些代码,其目的是教您如何使糟糕的内存管理变得糟糕-因此,我将其复制并粘贴并编译:
int main(int argc, char **argv) {
char *p, *q;
p = malloc(1024);
q = malloc(1024);
if (argc >= 2)
strcpy(p, argv[1]);
free(q);
free(p);
return 0;
}
用通用命令执行测试用例
/development/heapbug$ ./heapbug `perl -e 'print "A"x$K'`
对于$K < 1023
,我没想到会出现问题,但是对于$K = 1024
,我希望会发生一次核心转储,但没有发生。长话短说,我开始遇到$K > 1033
的段错误。
两个问题: 1)为什么会这样? 2)是否有一个公式可以说明系统的“公差”?
答案 0 :(得分:6)
当您写超出分配的内存范围时,您将调用undefined behavior。这意味着您无法准确预测程序的行为。它可能会崩溃,它可能会输出奇怪的结果,或者它似乎可以正常工作。
此外,进行看似无关的更改(例如添加未使用的局部变量或进行调试的printf
调用)也可以更改未定义行为的显示方式,就像使用其他编译器或使用具有不同优化功能的同一编译器进行编译一样设置。
仅仅因为程序可能崩溃并不意味着它会。
话虽这么说,可能发生的事情与在系统上实现malloc
的方式有关。它可能比对齐和簿记目的留出了更多字节。如果不进行积极的优化,那些多余的用于对齐的字节可能不会被用于其他任何事情,因此您在写入它们时会感到厌烦,但是当您进一步写入字节而不是包含malloc
和{ {1}}您已损坏。
但是同样,您不能依赖此行为。 C依赖于开发人员遵守规则,如果您不做不好,事情就会发生。
答案 1 :(得分:3)
未定义的行为就是这样。可能会崩溃。可能不会。它可能完美地工作。它可能会把冰箱里的牛奶全部喝掉。它可能会偷走您最喜欢的一双鞋,并与他们一起泥泞。
仅仅因为某些事情是未定义的行为,并不意味着它会立即变得如此明显。您在这里溢出了缓冲区,但未观察到后果。可能是因为您实际上没有使用分配的第二个缓冲区,所以如果您开始向其中写入数据,则对任何代码都没有影响。
这就是为什么存在诸如Valgrind之类的工具来查找可能并不总是会产生明显或不良结果的错误的原因。
答案 2 :(得分:1)
据我了解,如果您溢出到应用程序用户空间(代码/堆栈/等)中控制的内存中,则不能保证会导致核心转储,并且确实会覆盖其中的某些内存,这是由意外的缓冲区溢出。
一旦您开始尝试覆盖这些范围之外的数据,则操作系统更有可能阻止它。
答案 3 :(得分:1)
写入未分配的内存是未定义的行为。未指定结果。它可能会或可能不会导致崩溃。 heap overflow可能会破坏其他内存地址的内容,但是如何影响程序是未知的。