StackOverFlow延迟

时间:2013-10-01 10:17:51

标签: c

在Visual Studio 2012控制台应用程序中执行以下程序时:

#include <stdio.h>
int main() {
    int integer1, integer2, sum;

    char str[5];
    scanf("%s",str); /* Try to enter 10 chars */
    printf("%s\n",str);

    printf( "Enter first integer\n" );
    scanf( "%d", &integer1 );

    printf( "Enter second integer\n" );
    scanf( "%d", &integer2 );

    sum = integer1 + integer2;
    printf( "Sum = %d\n", sum );

    return 0;
}

它抛出异常“StackOverFlow”,这很明显,因为语句:

scanf("%s",str); /* Try to enter 10 chars */

我的问题是:为什么程序继续执行(通过打印str字符串,要求输入2个整数,求它们并打印结果),即使异常应该更早发生?

3 个答案:

答案 0 :(得分:3)

堆栈从高地址到低地址逐渐减少。一个CPU寄存器,堆栈指针跟踪堆栈的顶部 - 实际上是在最低地址,因为堆栈朝着较低的地址增长。编译器会查看您的函数(在本例中为main),并查看它需要多少自动存储,即存储局部变量。它生成代码以将堆栈指针减少函数所需的本地存储量。当函数被调用时,调用者将堆栈上的返回地址(递减堆栈指针)推送到堆栈,然后分支到被调用的函数,后者又递减堆栈指针(创建堆栈帧)地方变量的空间。

如果某个程序溢出了局部变量(就像你的那样),很可能会丢弃返回地址。由于堆栈向向低位地址增长,因此写入堆栈帧(朝向更高地址)将覆盖旧堆栈帧(调用方和调用方的调用方等)。

虽然main()是程序中要调用的第一个函数,但是已经有一个活动的堆栈帧,对应于main()的调用者,它是运行时环境。

在您的函数 main()尝试返回之前,不会注意到垃圾堆栈的任何副作用(如覆盖返回地址)。然后会发生什么事是任何人的猜测。如果返回地址被一个指向堆栈中某个位置的值覆盖,那么CPU将在那里进行分支,这是恶意代码的经典利用,利用缓冲区溢出和堆栈上分配的缓冲区。

这些链接有助于理解基于堆栈的缓冲区溢出:

http://www.tenouk.com/Bufferoverflowc/Bufferoverflow3.html http://en.wikipedia.org/wiki/Format_string_attack

最近的微处理器提供了一种防止数据执行的安全功能,一旦程序尝试返回指向数据的损坏地址(如堆栈),CPU就会引发异常。

http://en.wikipedia.org/wiki/NX_bit

enter image description here

答案 1 :(得分:1)

因为C不检查所有内容(什么?)。你的长字符串已经在堆栈上乱写了,当函数返回时,会注意到堆栈损坏。

值得注意的是,应始终使用safe versions of scanf类型函数。

答案 2 :(得分:1)

在C中,代码不能抛出异常。此外,scanf()不会检查堆栈。

可能发生的是Visual Studio为您的程序创建环境,包括设置堆栈。虽然它这样做,但它用一种模式填充堆栈。

main()返回时,将检查模式。只有在那个时候,C运行时才会注意到你已经破坏了堆栈。

结论:切勿使用scanf()sprintf()的不安全版本。运行时可能会捕获错误但是它会做得太晚,即使你收到错误消息,也不会帮助你找到它发生的时间。