为什么每次 运行 而不是固定数量的堆栈使用会发生堆栈溢出?
Why does a stack overflow occur at varying stack usage each run instead of a fixed amount?
我正在 运行在 Debian OS 上编写一个带有递归调用的程序。我的筹码量是
-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 字节,加上第一个部分页面,其可用大小在每个 运行.1
- 所有这些都在 "regular" 偏移量非零的情况下;如果您非常幸运并且随机偏移量为零,您可能正好得到两页。
我正在 运行在 Debian OS 上编写一个带有递归调用的程序。我的筹码量是
-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 字节,加上第一个部分页面,其可用大小在每个 运行.1
- 所有这些都在 "regular" 偏移量非零的情况下;如果您非常幸运并且随机偏移量为零,您可能正好得到两页。