为什么stackoverflow错误混乱? [英] Why are stackoverflow errors chaotic?

查看:98
本文介绍了为什么stackoverflow错误混乱?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个简单的C程序很少以相同的调用深度终止:

This simple C program rarely terminates at the same call depth:

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

void recursive(unsigned int rec);

int main(void)
{
  recursive(1);
  return 0;
}

void recursive(unsigned int rec) {
    printf("%u\n", rec);
    recursive(rec + 1);
}

这种混乱行为的背后可能是什么原因?

What could be the reasons behind this chaotic behavior?

我正在使用fedora(16GiB ram,堆栈大小为8192),并使用cc进行编译,而没有任何选项.

I am using fedora (16GiB ram, stack size of 8192), and compiled using cc without any options.

编辑

  • 我知道该程序将引发stackoverflow
  • 我知道启用某些编译器优化将改变行为,并且程序将达到整数溢出.
  • 我知道这是未定义的行为,这个问题的目的是了解/获得特定于实现的内部行为的概述,这些行为可以解释我们在那里观察到的情况.

更多的问题是,鉴于在Linux上线程堆栈大小是固定的并且由ulimit -s给出,是什么会影响可用的堆栈大小,以使stackoverflow不会总是在相同的调用深度发生?

The question is more, given that on Linux the thread stack size is fixed and given by ulimit -s, what would influence the available stack size so that the stackoverflow does not always occur at the same call depth?

编辑2 @BlueMoon在他的CentOS上总是看到相同的输出,而在我的Fedora上(堆栈为8M),我看到不同的输出(最后打印的整数261892或261845或261826或...)

EDIT 2 @BlueMoon always sees the same output on his CentOS, while on my Fedora, with a stack of 8M, I see different outputs (last printed integer 261892 or 261845, or 261826, or ...)

推荐答案

将printf调用更改为:

Change the printf call to:

printf("%u %p\n", rec, &rec);

这会迫使gcc将rec放在堆栈上,并为您提供其地址,这是对堆栈指针发生了什么的很好指示.

This forces gcc to put rec on the stack and gives you its address which is a good indication of what's going on with the stack pointer.

几次运行您的程序,并注意最后显示的地址是怎么回事.我的机器上的一些运行显示了这一点:

Run your program a few times and note what's going on with the address that's being printed at the end. A few runs on my machine shows this:

261958 0x7fff82d2878c
261778 0x7fffc85f379c
261816 0x7fff4139c78c
261926 0x7fff192bb79c

首先要注意的是,堆栈地址始终以78c79c结尾.这是为什么?跨越页面边界时,我们应该崩溃,页面长为0x1000字节,每个函数占用0x20字节的堆栈,因此地址应以00X或01X结尾.但是仔细看,我们在libc中崩溃了.因此,堆栈溢出发生在libc内的某个地方,由此可以得出结论,调用printf及其它调用的所有其他函数至少需要0x78c = 1932(可能加上X * 4096)字节的堆栈才能工作.

First thing to note is that the stack address always ends in 78c or 79c. Why is that? We should crash when crossing a page boundary, pages are 0x1000 bytes long and each function eats 0x20 bytes of stack so the address should end with 00X or 01X. But looking at this closer, we crash in libc. So the stack overflow happens somewhere inside libc, from this we can conclude that calling printf and everything else it calls needs at least 0x78c = 1932 (possibly plus X*4096) bytes of stack to work.

第二个问题是为什么要花费不同的迭代次数才能到达堆栈的末尾?一个提示是,在程序的每次运行中,我们获得的地址都是不同的.

The second question is why does it take a different number of iterations to reach the end of the stack? A hint is in the fact that the addresses we get are different on every run of the program.

1 0x7fff8c4c13ac
1 0x7fff0a88f33c
1 0x7fff8d02fc2c
1 0x7fffbc74fd9c

堆栈在内存中的位置是随机的.这样做是为了防止整个缓冲区溢出漏洞利用.但是由于内存分配(尤其是在此级别)只能在多个页面(4096字节)中完成,因此所有初始堆栈指针都将对齐为0x1000.这样可以减少随机堆栈地址中的随机位数,因此只需在堆栈顶部浪费随机字节数即可增加额外的随机性.

The position of the stack in memory is randomized. This is done to prevent a whole family of buffer overflow exploits. But since memory allocations, especially at this level, can only be done in multiple of pages (4096 bytes) all initial stack pointers would be aligned at 0x1000. This would reduce the number of random bits in the randomized stack address, so additional randomness is added by just wasting a random amount of bytes at the top of the stack.

操作系统只能在整个页面中说明您使用的内存量,包括堆栈上的限制.因此,即使堆栈从一个随机地址开始,堆栈上的最后一个可访问地址也将始终是以0xfff结尾的地址.

The operating system can only account the amount of memory you use, including the limit on the stack, in whole pages. So even though the stack starts at a random address, the last accessible address on the stack will always be an address ending in 0xfff.

简单的答案是:为了增加随机存储器布局中的随机性,故意浪费了堆栈顶部的一堆字节,但是堆栈的末尾必须在页面边界结束.

The short answer is: to increase the amount of randomness in the randomized memory layout a bunch of bytes on the top of the stack are deliberately wasted, but the end of the stack has to end on a page boundary.

这篇关于为什么stackoverflow错误混乱?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆