一、什么是线程栈溢出
我们都知道,每一个win32线程都会开辟一个空间,用来临时存储线程执行时所调用的一系列函数的参数、返回地址和局部变量及其他上下文信息。这个空间就是线程的栈区。栈区的容量是有限的,在程序编译链接时,就固定下来了。通过VC++编译的程序,默认的栈区大小是1MB。当我们程序执行时,访问超过了这个空间的边界,就叫栈溢出,又叫Stack overflow。这时会产生代码为STATUS_STACK_OVERFLOW(0xC00000FD)的异常,从而导致程序崩溃。注意一定要与栈缓冲区溢出---STATUS_STACK_BUFFER_OVERRUN(0xC0000409)区别开来。
二、栈溢出的原因
栈溢出是用户模式线程可能会遇到的错误。 有三个可能的原因产生此错误:
线程使用为其保留的整个堆栈。这通常是由无限递归引起的。
线程无法扩展堆栈,因为页文件已最大化,因此无法提交其他页来扩展堆栈。
由于系统内使用以扩展页面文件的短时间内,线程不能扩展堆栈。
当一个线程上运行的函数分配的本地变量时,变量放线程的调用堆栈上。 函数所需的堆栈空间量可能大至所有本地变量的大小的总和。 但是,编译器通常会执行优化,降低函数所需的堆栈空间。 例如,如果两个变量,则在不同的作用域中,编译器可以为这两个这些变量使用相同的堆栈内存。 编译器还可能无法完全消除某些本地变量对计算进行优化。优化的量会影响应用在生成时的编译器设置。 例如,调试版本和发布版本具有不同的优化级别。 所需的调试版本中的函数的堆栈空间量可能会大于该发行版中的相同函数所需的堆栈空间量。
在我们编写代码时,如下情况通常引发栈溢出:
三、溢出代码举例
3.1、在栈里分配了很大的缓冲区导致溢出
代码如下:
#include "stdafx.h" int LargeBuffer(void)
{
char buf[1024 * 1024];
int a = 0;
int b = buf[2];
return a+b;
}
int _tmain(int argc, _TCHAR* argv[])
{
int n=LargeBuffer();
printf("n=%d\n", n);
return 0;
}
由于在VS里默认线程栈空间为1MB,我们代码里在栈区分配了一个刚好1MB的数组,在加上其他函数入栈占用的空间,肯定就会超出1MB产生溢出
在VS2013里编译,Debug模式下调试运行,果然报错,跟我们前面预想的一样
我们注意到此时的调用栈如下:
看此时的栈,没又被破坏,最后停在了_chkstk()里,这是个什么样的函数呢。这个函数其实是VC++的crt函数,用来检测堆栈的。停在这里,说明这个函数检测到了这种情况栈溢出。我们看看它的代码,在VS里转到汇编模式下
在地址栏里输入上面错误提示框里地址0x00F21767,得到
_chkstk: push ecx cmp eax,1000h lea ecx,[esp+8] jb lastpage probepages: sub ecx,1000h sub eax,1000h test dword ptr [ecx],eax cmp eax,1000h jae probepages (0040818c) lastpage: sub ecx,eax mov eax,esp test dword ptr [ecx],eax mov esp,ecx mov ecx,dword ptr [eax] mov eax,dword ptr [eax+4] push eax ret
大概流程
push ebp mov ebp,esp mov eax,1000D4h call _chkstk()
上面讲的是在vs里的debug模式下运行时的情况,那么在Release模式下是怎样的呢?我们转到Release模式下,编译运行,同样报错
可以看到,跟Debug模式一样。也就是说,如果是这种是在栈区分配了超大缓冲区导致异常,基本都会被_chkstk()检测出来。
针对这种情况,通常我们可以用下面两种方式来解决:
原文:https://www.cnblogs.com/yilang/p/11361155.html