微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

我在Debian操作系统上运行一个递归调用的程序.我的堆栈大小是
-s: stack size (kbytes)             8192

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

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

fn print_till_zero(x: &mut i32) {
    *x -= 1;
    println!("Variable is {}",*x);
    while *x != 0 {
        print_till_zero(x);
    }
}

并且该值被传递

static mut Y: i32 = 999999999;
unsafe {
    print_till_zero(&mut Y);
}

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

运行1:

====snip====
Variable is 999895412
Variable is 999895411

thread 'main' has overflowed its stack
fatal runtime error: stack overflow

运行2:

====snip====
Variable is 999895352
Variable is 999895351

thread 'main' has overflowed its stack
fatal runtime error: stack overflow

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

#pragma GCC push_options
#pragma GCC optimize ("O0")
#include<stdio.h>
void rec(int i){
    printf("%d,",i);
    rec(i-1);
    fflush(stdout);
}
int main(){
setbuf(stdout,NULL);
rec(1000000);
}
#pragma GCC pop_options

输出

运行1:

738551,738550,[1]    7052 segmentation fault

运行2:

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个字节,加上一个分页面,其可用大小在每次运行时都不同.

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

原文地址:https://www.jb51.cc/c/116695.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐