有没有一种标准的方法来查看您的应用程序有多少堆栈空间以及运行期间堆栈使用的最高水印?
另外在可怕的情况下实际溢出会发生什么?
它会崩溃,触发异常还是发出信号?是否有标准或在所有系统和编译器上有所不同?
我正在寻找专门针对Windows,Linux和Macintosh的.
在Windows上,将生成堆栈溢出异常.
以下Windows代码说明了这一点:
#include#include void StackOverFlow() { CONTEXT context; // we are interested control registers context.ContextFlags = CONTEXT_CONTROL; // get the details GetThreadContext(GetCurrentThread(), &context); // print the stack pointer printf("Esp: %X\n", context.Esp); // this will eventually overflow the stack StackOverFlow(); } DWORD ExceptionFilter(EXCEPTION_POINTERS *pointers, DWORD dwException) { return EXCEPTION_EXECUTE_HANDLER; } void main() { CONTEXT context; // we are interested control registers context.ContextFlags = CONTEXT_CONTROL; // get the details GetThreadContext(GetCurrentThread(), &context); // print the stack pointer printf("Esp: %X\n", context.Esp); __try { // cause a stack overflow StackOverFlow(); } __except(ExceptionFilter(GetExceptionInformation(), GetExceptionCode())) { printf("\n****** ExceptionFilter fired ******\n"); } }
运行此exe时,将生成以下输出:
Esp: 12FC4C Esp: 12F96C Esp: 12F68C ..... Esp: 33D8C Esp: 33AAC Esp: 337CC ****** ExceptionFilter fired ******
在Linux上,如果代码尝试写入堆栈,则会出现分段错误.
堆栈的大小是进程之间继承的属性.如果你可以阅读或修改它在使用类似的命令壳ulimit -s
(中sh
,ksh
,zsh
)或limit stacksize
(tcsh
,zsh
).
从程序中,可以使用读取堆栈的大小
#include#include struct rlimit l; getrlimit(RLIMIT_STACK, &l); printf("stack_size = %d\n", l.rlim_cur);
我不知道获得可用堆栈大小的标准方法.
堆栈首先argc
跟着argv
环境的内容和副本,然后是你的变量.但是因为内核可以随机化堆栈起始位置,并且上面可能存在一些虚拟值argc
,所以假设您在l.rlim_cur
下面有可用字节是错误的&argc
.
检索堆栈的确切位置的一种方法是查看文件/proc/1234/maps
(1234
程序的进程ID 在哪里).一旦了解了这些边界,就可以通过查看最新局部变量的地址来计算堆栈的使用量.
gcc在"不安全"函数调用中的返回地址和正常变量之间放置了一个额外的内存块,就像(在这个例子中函数是void test(){char a [10]; b [20]}:
call stack: ----------- return address dummy char b[10] char a[20]
如果函数在指针'a'中写入36个字节,则溢出将"损坏"返回地址(可能的安全漏洞).但它也会改变'dummy'的值,即指针和返回地址之间的值,因此程序会崩溃并发出警告(你可以使用-fno-stack-protector禁用它)
在Linux上,Gnu libsigsegv库包含该函数stackoverflow_install_handler
,该函数可以检测(并在某些情况下帮助您从中恢复)堆栈溢出.
在Windows上,堆栈(针对特定线程)按需增长,直到达到为此线程创建之前指定的堆栈大小.
按需增长是使用保护页面实现的,因为最初只有一个堆栈片段可用,然后是一个保护页面,当被命中时会触发异常 - 这个异常是特殊的,并由系统处理you - 处理增加了可用的堆栈空间(如果已达到限制,也会检查!)并重试读取操作.
一旦达到限制,就不再增长,导致堆栈溢出异常.当前堆栈基数和限制存储在线程环境块中,称为_NT_TIB
(线程信息块).如果你有一个方便的调试器,这就是你看到的:
0:000> dt ntdll!_teb @$teb nttib. +0x000 NtTib : +0x000 ExceptionList : 0x0012e030 _EXCEPTION_REGISTRATION_RECORD +0x004 StackBase : 0x00130000 +0x008 StackLimit : 0x0011e000 +0x00c SubSystemTib : (null) +0x010 FiberData : 0x00001e00 +0x010 Version : 0x1e00 +0x014 ArbitraryUserPointer : (null) +0x018 Self : 0x7ffdf000 _NT_TIB
StackLimit属性将按需更新.如果检查此内存块上的属性,您将看到类似的内容:
0:000> !address 0x0011e000 00030000 : 0011e000 - 00012000 Type 00020000 MEM_PRIVATE Protect 00000004 PAGE_READWRITE State 00001000 MEM_COMMIT Usage RegionUsageStack Pid.Tid abc.560
检查旁边的页面会显示guard属性:
0:000> !address 0x0011e000-1000 00030000 : 0011d000 - 00001000 Type 00020000 MEM_PRIVATE Protect 00000104 PAGE_READWRITE | PAGE_GUARD State 00001000 MEM_COMMIT Usage RegionUsageStack Pid.Tid abc.560
希望能帮助到你.