我了解到每个进程的默认堆栈大小限制为 8MB,并且 mmap_base 是根据 rlimit 中的堆栈大小和随机值计算的。下面的代码是 mmap_base 函数,它计算 x86 中的 mmap_base 地址(linux/include/uapi/asm-generic/resource.h)。
static unsigned long mmap_base(unsigned long rnd)
{
unsigned long gap = rlimit(RLIMIT_STACK);
if (gap < MIN_GAP)
gap = MIN_GAP;
else if (gap > MAX_GAP)
gap = MAX_GAP;
return PAGE_ALIGN(TASK_SIZE - gap - rnd);
}
我想知道如果程序堆栈大小大于 8MB+rnd 值怎么办?我的意思是如果堆栈大小增长到 mmap_base 以上怎么办?如果我分配超过 8MB 的堆栈内存,它是否会因分段错误而失败?如果内核自动扩大堆栈大小,是否可以将 mmap_base 中的内容移动到其他空间?
进程主线程堆栈大小不能增长大于设置的限制。此限制的默认值为 8 MB。超过此限制将导致分段错误,并且该进程将被发送一个
SIGSEGV
信号,默认情况下将其终止。ulimit -s
在启动程序之前可以更改堆栈的最大大小。程序启动后,内核不会在内存区域(如 mmap 区域)周围移动,也不能这样做,因为通常有指向该区域的指针在移动后会指向错误的地址。但是,堆栈溢出的检查是在访问堆栈内存时执行的,因此只是在堆栈上执行大分配,或者以其他方式更改堆栈指针的值,并不一定会触发故障。
2017 年夏天有一些关于利用这种行为的可能性的讨论。如果某些攻击者可以欺骗程序分配大量内存,这可能导致堆栈指针跳过保护区并指向有效但不同的区域。这为一些巧妙的技巧提供了机会来控制该过程。有关该问题的讨论,请参阅此 lwn.net 文章。