I thought I could get the beginning of my process stack by taking the address of a variable in main and rounding up to a page boundary (considering that my stack grows down).
I compared this to the boundary reported by /proc/self/maps
and it's always off by 1, 2, or 3 pages (4096 bytes per page), never by a different offset. The difference varies with each run and this C program used in the following (messy, not minimalistic) pipeline demonstrates the difference.
stacksz.c:
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#define CAT "cat /proc/XXXXXXXXXXX/maps"
#define CATP "cat /proc/%ld/maps"
#define MASK ((sizeof(char)<<12)-1)
int main()
{
uintptr_t top = (uintptr_t)&top + MASK & ~MASK;
char cat[sizeof CAT];
sprintf(cat,CATP,(long)getpid());
if(system(cat)) return 1;
printf(" %lx stack\n", top);
return 0;
}
bash pipeline:
gcc stacksz.c && echo "$(( $(./a.out |grep stack |tr '-' ' ' |cut -d' ' -f2 |sed 's/^/0x/'|tr '\n' -|sed 's/-$//') ))"
I'm curious if anyone can explain this phenomenon.
The machine is Linux precision 4.15.0-43-generic #46-Ubuntu SMP x86_64
.
I got the following offset distribution in 1000 runs:
4096 195
8192 490
12288 315
ASLR first completely randomizes the stack location in the virtual memory. But it does more: It also randomizes the stack pointer relative to the top of the stack mapping!
From the linux source code:
unsigned long arch_align_stack(unsigned long sp)
{
if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
sp -= get_random_int() % 8192;
return sp & ~0xf;
}
Here, if ASLR is active, the stack pointer is reduced by 0-8192 bytes and then 16 bytes aligned. This explains the variable offset of 1-3 pages.
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