桌面操作系统上的C编译器使用多少个内存页来检测堆栈溢出?

这个问题与C99中的可变长度数组有关但不同。

答案指出,在堆栈中分配可变长度数组(或者只是固定大小的大数组)的一个危险是分配可能会无声地失败,而不是调用malloc ,后者明确告诉调用者分配是否成功。

现代非嵌入式编译平台使用无效的内存区域来检测一些堆栈溢出,而无需额外成本(检查只是MMU已经免费进行的检查)。 这不能保证100%免受上述问题的影响,因为非常大的本地数组可能导致堆栈指针跳过无效区域。

是否有人知道通常为此检测分配了多少页? 我想这至少是4KiB,但它可能会更多。 这是由编译器或操作系统做出的选择,在任何一种情况下,有没有办法改变它?

在Windows上,它是一个4KB页面(至少在x86上):请参阅基于Windows NT的应用程序的堆栈检查说明 。

这种自动增长方法使用一个保护页面,一个保留的,未提交的内存页面,它与内存的已提交部分相邻。 当应用程序触及保护页面时,操作系统将提交该页面,下一个未提交的页面将成为新的保护页面。 自动堆栈增长仅适用于保护页面,堆栈内存必须以4K或一页增量增长。 如果应用程序在触及保护页面之前触及另一个保留但未提交的堆栈内存页面,则会发生正常的页面错误exception,并且可能导致不可预测的行为。

为防止出现故障,每次本地分配超过4K时,编译器都会调用__chkstk()函数。 Windows NT __chkstk()函数不会像MS-DOS版本那样显式检查堆栈溢出。 它只是触发从当前堆栈指针位置到请求分配的每4K的内存地址。 这会以正确的顺序触发保护页面,并根据需要向堆栈提交额外的内存。

对于GCC, GCC堆栈检查

我不确定C99的VLA是如何改变WinNT行为的。

我很确定最常见的做法是只使用一页,通常是4k。 但是,一个好的编译器将依次尝试访问大于函数入口(或VLA / alloca分配)上的页面大小的堆栈帧的每个页面,以确保命中一个保护页面。 GCC可以选择这样做; 请参阅: http : //gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#Code-Gen-Options和-fstack-check选项。