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

时间:2011-04-04 19:27:35

标签: c c99 variable-length-array mmu

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

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

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

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

2 个答案:

答案 0 :(得分:7)

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

答案 1 :(得分:6)

在Windows上,它是一个4KB的页面(至少在x86上):见Description of the stack checking for Windows NT-based applications

  

这种自动增长方法使用了   保护页面,保留,未提交,   与之相邻的内存页面   记忆的承诺部分。什么时候   申请触及警卫   页面,操作系统提交   那个页面和下一个未提交的   页面成为新的防护页面。   自动堆栈增长仅适用于   保护页面和堆栈内存必须   以4K或一页为增量增长。   如果应用程序接触另一个   堆栈的保留但未提交的页面   在触及守卫之前的记忆   页面,正常页面错误异常   发生和不可预测的行为可以   结果。

     

...

     

为了防止故障,编译器   每次调用__chkstk()函数   本地分配超过4K的时间。   Windows NT __chkstk()函数   没有明确检查堆栈   像MS-DOS版本那样溢出。   它只是触及内存地址   当前堆栈中的每个4K   指针所在的位置   分配。这触发了警卫   页面按正确顺序排列   向堆栈提交额外的内存   根据需要。

对于GCC,GCC Stack checking

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