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 I've 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 with ulimit
.
The recursive function is a decrements 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 theoretically must not change, I was expecting a stack overflow at the same value each time, but it is not, 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; a similar behavior is observed in 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
Output:
Run 1:
738551,738550,[1] 7052 segmentation fault
Run 2:
738438,738437,[1] 7125 segmentation fault
Stack overflow in computer programming occurs when more items than a stack can hold are added. Explore errors, exceptions, and causes of stack overflow and learn about stack push, pop, underflow, and overflow.
Oops. Another cause of stack overflow is when there simply isn't enough physical memory, even if we allocated plenty for our stack (stacks usually grow up or down and eventually run into other stuff in memory). The most likely culprit though, is something known as recursion.
The only valid answer is vague: "too much is when the stack overflows." Unless you are in complete control over the implementation of every line of code between the program's entry point and the function in question, you can make no assumptions about how much stack is available.
If the sum of your stack variables (including low-level overhead such as return addresses, stack-based arguments, return value placeholders and alignment bytes) in the entire call stack exceeds that limit, you get a stack overflow, which typically takes down your program without any chance at recovery. A few kilobytes are usually fine.
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 is the biggest alignment requirement on x86 and almost any other platform I know).
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 the 8192 bytes you requested, plus the first partial page, whose available size is different at each run.1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With