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

查看:42
本文介绍了为什么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
", 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 给出,什么会影响可用堆栈大小,从而导致堆栈溢出不会总是同时发生调用深度?

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
", 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天全站免登陆