nohup nohup -4 years ago 89
Linux Question

Occurring stack overflow at varying stack usage and not at fixed stack usage on each run

Apologies for the confusing title, this is the best I could come up with.

I am running a program with a recursive call on a debian OS. My stack size is

-s: stack size (kbytes) 8192


As far as what I learned, the stack size must be fixed, and should be the same that must be allocated to a program at every run unless it is explicitly changed in the
ulimit
.

The code to the recursive function is a simple decrement to a given number until it reaches
0
. This is written in rust.

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


and the value is passed as

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


Since the stack allocated to the program is fixed, and must not change theoretically, I was expecting a stackoverflow at the same variable each time, but it is not happening at the same variable, which means the stack allocation is variadic.

Run 1:

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

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


Run 2:

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

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


Although the difference is subtle, shouldn't it be ideally causing the stack overflow at the same variable? Why is it happening at different times, implying different stack size for each run? This is not specific to rust, and a similar behavior is observed in
C
too.

Edit::

C code is

#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


Output:

Run 1:

738551,738550,[1] 7052 segmentation fault


Run 2:

738438,738437,[1] 7125 segmentation fault

Answer Source

Most probably this is due to ASLR.

The base address of the stack is randomized at each run to make certain types of exploits more difficult; on Linux this has a granularity of 16 bytes (which incidentally is the biggest alignment requirement on x86).

On the other hand, the page size is (normally?) 4 KB on x86, and the system detects the stack overflow when you touch the first forbidden page; this means that you'll always have available a partial page first (with the offset depending from ASLR), and then two full pages before the system detects a stack overflow. Hence the total usable stack size is at least 8192 bytes, plus the first partial page, whose available size is different at each run.

(if you are very lucky and the calculated offset is zero you probably get exactly two pages)

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download