如果我覆盖堆栈上的返回地址会发生什么?

MMM*_*VII 4 c c++ stack-overflow

我知道这是一种危险的行为,我只是想知道发生了什么.

代码如下:

#include<stdio.h>
#include<stdlib.h>

static int count = 0;

void hello(void){
    count ++;  
    fprintf(stderr,"hello! %d\n",count);
}

void foo(void){
    void *buf[10];
    static int i;

    for(i = 0 ; i < 100 ; i++){ // put enough data to overwrite the return address on stack
        buf[i] = hello;
    }
}

int main(void){
    int buf[1000];
    foo();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

……
hello! 83
hello! 84
hello! 85
hello! 86
hello! 87
hello! 88
hello! 89
Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud)

为什么hello func被称为89次?当foo返回时,pc寄存器获取hello func的地址,不是吗?所以你好叫,然后做什么.那又怎样?程序是不是回到主要功能?89来自哪里?我必须误解一些事情,请指出.

ant*_*ron 9

您将函数的地址写入hello堆栈的foo90次,超出了您预留的空间量buf.当foo试图返回时,它将(第一个)额外地址中的一个加载hello到程序计数器中,弹出它,然后"返回"它.我说"其中一个"的原因是因为在您保留的数组的末尾和返回地址的开头之间的堆栈上可能存在一些空间.

hello假设它被称为foo.但是,它没有被调用,换句话说,其余部分的地址foo没有被推到堆栈上hello以便返回.因此,当hello返回时,弹出的东西是堆栈中的下一个东西,它又是地址hello.

这看起来像是一个调用hello,但它实际上并不是一个调用,因为它是一个返回,所以每次都hello返回自己并hello从堆栈中弹出一个地址,直到它用完了这些地址.

在那些用完之后,堆栈上的下一个字节,当你被视为指针时,你没有被覆盖,它们被存储在内存中的其他位置,可能是未分配给你的进程内存映射的东西.hello试图回到那个,那就是程序发生错误的时候.


Nah*_*hum 5

那么这是未定义的行为.当您进入未分配的内存时,任何事情都可能发生,具体取决于此时的"垃圾".

分段错误基本上意味着你走出程序的受保护内存(在你的情况下,它需要"89次").您的操作系统会对此进行保护,并告诉您,您尝试在为该程序分配的空间之外进行编写.

(在旧操作系统中不是这种情况,如果程序溢出到系统空间,这样的错误可能会导致操作系统崩溃.)