Linux上的堆栈起始位置

8

我认为可以通过获取main函数中变量的地址并向上取整到页面边界来获得我的进程堆栈的起始位置(考虑到我的堆栈是向下增长的)。

我将此与/proc/self/maps报告的边界进行比较,它总是偏移1、2或3页(每页4096字节),从未有不同的偏移。差异随着每次运行而变化,并且以下C程序在使用的管道中(混乱而不简洁)演示了这种差异。

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管道:

gcc stacksz.c && echo "$(( $(./a.out |grep stack |tr '-' ' ' |cut -d' ' -f2 |sed 's/^/0x/'|tr '\n' -|sed 's/-$//') ))"

我很好奇是否有人能解释这个现象。 这台机器是Linux precision 4.15.0-43-generic #46-Ubuntu SMP x86_64
我在1000次运行中得到了以下的偏移量分布:
4096 195
8192 490
12288 315

1
你开启了ASLR吗? - Eugene Sh.
你用调试器查看过栈里的内容吗? - zneak
@ASLR 每次运行时所有地址都会改变,所以我想是的。 - Petr Skocik
(顺便说一下,您可以使用/proc/self/maps。) - Kerrek SB
1
哦,在这里,如果你选择一个不同的本地变量,你会得到更符合预期的结果:https://wandbox.org/permlink/tO5WuOAb3SdCed3v - Kerrek SB
显示剩余7条评论
1个回答

5

ASLR首先完全随机化虚拟内存中的堆栈位置。但它还做了更多:它还将堆栈指针相对于堆栈映射的顶部进行随机化!

来自Linux源代码:

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;
}

如果ASLR被激活,栈指针会减少0-8192字节,然后16字节对齐。这解释了1-3页的可变偏移量。


RSP移动的其他事情:必须有指向argv[]envp[]的字符串的空间,可能在映射的顶部。指针数组直接放在堆栈上,在进入用户空间时位于堆栈指针值的正上方。(SP指向argc;在其上方是argv[0],然后是argv[1]等,然后是0,然后是envp[0]等)。此外,OP的程序在几个从CRT启动代码调用使用几个堆栈空间插槽之后,在main中报告RSP。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接