win32 thread stack overflow problems (a)

First, what is the thread stack overflow

We all know that every win32 thread will open up a space for temporary storage of parameter set of functions called by a thread execution, return address and local variables and other context information. This space is the thread stack area. The capacity of the stack area is limited, when the program is compiled and linked, it is fixed down. By VC ++ compiled programs, the default stack area size is 1MB. When we execute the program, visit this space over the border, called stack overflow, also known as Stack overflow . At this time will generate code STATUS_STACK_OVERFLOW (0xC00000FD) abnormalities, leading to crashes. Attention must be with a stack buffer overflow --- STATUS_STACK _BUFFER_OVERRUN (0xc0000409) distinguished.

Second, the cause of stack overflow

Stack overflow error is a user-mode thread might encounter. There are three possible causes of this error:

  • Thread reserved for the use of the entire stack. This is usually caused by infinite recursion.

  • Thread stack can not be extended because the page file is maximized, and therefore can not submit additional pages to extend the stack.

  • Due to the use of the system in a short period of time extensions page file, the thread can not be extended stack.

When a local variable functions assigned to run on a thread, put on the call stack variable thread. Function of the amount of stack space required may be large to the size of the sum of all the local variables. However, the compiler would normally perform optimization, reducing the required function stack space. For example, if two variables are in a different scope, the compiler can use the same stack memory for both these variables. The compiler also may not completely eliminate some local variables to calculate optimized. The application will affect the amount of optimization in the generation of compiler settings. For example, debug and release versions with different levels of optimization. A function of the amount of stack space required for debug builds may be greater than the amount of stack space required for the release of the same functions.

When we write code, as usually caused by a stack overflow:

  • For some reason, causes the function recursion depth deep or infinite recursion
  • In the stack allocated large buffer
  • The stack of a buffer overflow

Third, the spill code Examples

3.1, in the stack buffer allocated a great cause an overflow

code show as below:

#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;
}

Since the default thread stack space is 1MB VS years, we in the stack area code assigned to an array of just 1MB, plus stack space occupied by other functions, it will certainly exceed 1MB overflow

Commissioning in VS2013 in compiling, Debug mode, and sure enough error, just like we expected earlier

 

We note the call stack at this time is as follows:

Look stack at this time, but not destroyed, and finally stopped at the _chkstk (), this is what kind of function it. This function is actually a crt VC ++ function to detect stack. Stop here, indicating that this function detects this situation stack overflow. We look at its code to compile in VS mode where

In the address bar enter the above error message box address 0x00F21767, get

 

_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

大概流程

  1. chkstk例程是C编译器的助手例程。对于x86当局部变量超过4096字节;对于x64编译器,它分别是4K和8K。即, 当一个函数局部变量超过一个页面大小4k的时候, 编译器会自动插入这个函数。插入这个函数的位置在:
    push        ebp
    mov         ebp,esp
    mov         eax,1000D4h
    call       _chkstk()
  2. 这个函数做什么?当函数为堆栈分配的页面不够时, (堆栈默认大小为1M), 堆栈需要更多的页面时调用这个函数。当堆栈使用大于分配的大小(默认1M)时,产生_XCPT_UNABLE_TO_GROW_STACK.
  3. 当调用这个函数时, 首先外面对eax进行赋值(已经分配的堆栈大小 + 即将分配给函数局部变量的堆栈大小)
  4. 调用 _chkstk() 首先,保持当期esp到eax中,然后开始判断:如果分配的大小大于一个页面, 到第5)步(大多都先第5)步);否则到第6)步。
  5. 当需要分配的大小大于一个页面, 则增加一个页面。“sub eax 1000h” 表示堆栈栈顶下移1000h,[由于堆栈是高地址(栈底部)->低地址(栈顶部)分布],所以堆栈扩大了1000h; “sub ecx 1000h”表示分配了1000h(1个页面)之后还需要多少空间;“test dword ptr [ecx],eax” 表示分配空间,这个时候之前只不过是分配虚存,内存没有 commit  ,这个时候对这个内存地址进行读写操作都会引发一个 page fault 异常(_XCPT_GUARD_PAGE_VIOLATION),  OS捕获这个异常,检查一定的条件,适合的时候就把这个内存页 commit 了,即分配了实际的物理内存。然后再次比较需要多少内存,如果还是超过1页(1000h), 则重复第5)步,否则到第6)步。
  6. 还需要分配的堆栈空间小于1页的时候,“sub  ecx,eax” 堆栈继续扩大(扩大了剩余大小的空间);然后“mov  esp,ecx”,保存到原来的esp;并且通过“test”为堆栈分配空间。
  7. 最后,esp的值不再是原来的值,堆栈的大小变成: 堆栈原来大小 + 局部变量需要的堆栈大小 + xx (push 用的一点堆栈的零头).

 上面讲的是在vs里的debug模式下运行时的情况,那么在Release模式下是怎样的呢?我们转到Release模式下,编译运行,同样报错

可以看到,跟Debug模式一样。也就是说,如果是这种是在栈区分配了超大缓冲区导致异常,基本都会被_chkstk()检测出来。

针对这种情况,通常我们可以用下面两种方式来解决:

  • 修改线程栈区大小,重新编译程序。这种方法只能对我们确定要使用多少栈区的情况下有用,要不然,不能彻底解决。
    在VS里可以在这里设置一个比现在大的栈区
  • 更好的方式是把这种需要的大缓冲区反倒进程的堆里区申请使用。

 

Guess you like

Origin www.cnblogs.com/yilang/p/11361155.html