2

在 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 个整数,将它们相加并打印结果)即使异常应该更早发生?

4

3 回答 3

4

堆栈向下增长,从高地址到低地址。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

在此处输入图像描述

于 2013-10-01T10:42:25.820 回答
1

因为 C 不会检查所有内容(任何内容?)。您的长字符串已在堆栈上乱涂乱画,当函数返回堆栈损坏时会注意到。

值得注意的是,应该始终使用安全版本的类型函数。 scanf

于 2013-10-01T10:21:20.110 回答
1

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

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

返回时main(),检查模式。只有在那个时候,C 运行时才会注意到您丢弃了堆栈。

结论:永远不要使用 和 的不安全scanf()版本sprintf()。运行时可能会捕获错误,但它会为时已晚,即使您收到错误消息,也无法帮助您找出错误发生的时间。

于 2013-10-01T10:22:04.637 回答