On modern Linux machines ASLR is enabled by default. As a result, when the process image for the binary is created in virtual memory, the base address of the stack is located at a random offset:
/*
* These are the functions used to load ELF style executables and shared
* libraries. There is no binary dependent code anywhere else.
*/
#ifndef STACK_RND_MASK
#define STACK_RND_MASK (0x7ff >> (PAGE_SHIFT - 12)) /* 8MB of VA */
#endif
static unsigned long randomize_stack_top(unsigned long stack_top)
{
unsigned long random_variable = 0;
if (current->flags & PF_RANDOMIZE) {
random_variable = get_random_long();
random_variable &= STACK_RND_MASK;
random_variable <<= PAGE_SHIFT;
}
#ifdef CONFIG_STACK_GROWSUP
return PAGE_ALIGN(stack_top) + random_variable;
#else
return PAGE_ALIGN(stack_top) - random_variable;
#endif
}
We can test this with a simple program that prints the memory address of a local variable in function main()
:
#include <stdio.h>
int main(void){
int i = 3;
printf("%p\n", &i);
return 0;
}
compile and execute:
$ gcc -m32 -o print_stack_address print_stack_address.c
$ ./print_stack_address
0xff9dc78c
$ ./print_stack_address
0xff832d3c
$ ./print_stack_address
0xff844c1c
$ ./print_stack_address
0xff999e0c
$ ./print_stack_address
0xffd1117c