为什么堆栈溢出发生在不同的堆栈使用,每次运行而不是固定的数量?

前端之家收集整理的这篇文章主要介绍了为什么堆栈溢出发生在不同的堆栈使用,每次运行而不是固定的数量?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我在Debian操作系统上运行一个递归调用的程序.我的堆栈大小是
  1. -s: stack size (kbytes) 8192

据我了解,堆栈大小必须是固定的,并且应该是相同的,必须分配给每个运行的程序,除非用ulimit显式更改.

递归函数是递减一个给定的数字,直到它达到0.这是写在Rust中.

  1. fn print_till_zero(x: &mut i32) {
  2. *x -= 1;
  3. println!("Variable is {}",*x);
  4. while *x != 0 {
  5. print_till_zero(x);
  6. }
  7. }

并且该值被传递

  1. static mut Y: i32 = 999999999;
  2. unsafe {
  3. print_till_zero(&mut Y);
  4. }

由于分配给程序的堆栈是固定的,理论上不能改变,我期望每次堆栈溢出在相同的值,但不是,这意味着堆栈分配是可变的.

运行1:

  1. ====snip====
  2. Variable is 999895412
  3. Variable is 999895411
  4.  
  5. thread 'main' has overflowed its stack
  6. fatal runtime error: stack overflow

运行2:

  1. ====snip====
  2. Variable is 999895352
  3. Variable is 999895351
  4.  
  5. thread 'main' has overflowed its stack
  6. fatal runtime error: stack overflow

虽然区别是微妙的,不应该理想地导致堆栈溢出在同一个变量?为什么在不同的时间发生,这意味着每次运行的堆栈大小不一样?这不属于Rust;在C中观察到类似的行为:

  1. #pragma GCC push_options
  2. #pragma GCC optimize ("O0")
  3. #include<stdio.h>
  4. void rec(int i){
  5. printf("%d,",i);
  6. rec(i-1);
  7. fflush(stdout);
  8. }
  9. int main(){
  10. setbuf(stdout,NULL);
  11. rec(1000000);
  12. }
  13. #pragma GCC pop_options

输出

运行1:

  1. 738551,738550,[1] 7052 segmentation fault

运行2:

  1. 738438,738437,[1] 7125 segmentation fault

解决方法

很可能是由于 ASLR.

堆栈的基地址在每次运行时被随机化,以使某些类型的攻击更加困难;在Linux这个has a granularity of 16 bytes(这是x86和几乎任何其他平台我知道的最大的对齐要求).

另一方面,the page size is (normally) 4 KB on x86,当您触摸第一个禁止页面时,系统检测到堆栈溢出;这意味着您始终可以首先使用部分页面(偏移量取决于ASLR),然后系统检测到堆栈溢出之前的两个完整页面.因此,总可用堆栈大小至少为您请求的8192个字节,加上第一个部分页面,其可用大小在每次运行时都不同.

>所有这些在“正常”情况下,偏移量不为零;如果你很幸运,随机偏移量为零,你可能会得到两页.

猜你在找的C&C++相关文章