Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Beginning of stack on Linux

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
like image 888
PSkocik Avatar asked Feb 05 '19 21:02

PSkocik


1 Answers

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.

like image 157
Ctx Avatar answered Nov 04 '22 10:11

Ctx