如何检测 ac/c++ 程序中可能/潜在的堆栈溢出问题?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/199747/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to detect possible / potential stack overflow problems in a c / c++ program?
提问by KPexEA
Is there a standard way to see how much stack space your app has and what the highest watermark for stack usage is during a run?
是否有一种标准方法可以查看您的应用程序有多少堆栈空间以及在运行期间堆栈使用的最高水印是多少?
Also in the dreaded case of actual overflow what happens?
同样在实际溢出的可怕情况下会发生什么?
Does it crash, trigger an exception or signal? Is there a standard or is it different on all systems and compilers?
它是否崩溃、触发异常或信号?是否有标准或在所有系统和编译器上都不同?
I'm looking specifically for Windows, Linux and Macintosh.
我正在专门寻找 Windows、Linux 和 Macintosh。
回答by jussij
On Windowsa stack overflow exceptionwill be generated.
在Windows 上,将生成堆栈溢出异常。
The following windows code illustrates this:
以下 Windows 代码说明了这一点:
#include <stdio.h>
#include <windows.h>
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");
}
}
When this exe is run the following output is generated:
运行此 exe 时,会生成以下输出:
Esp: 12FC4C
Esp: 12F96C
Esp: 12F68C
.....
Esp: 33D8C
Esp: 33AAC
Esp: 337CC
****** ExceptionFilter fired ******
回答by adl
On Linux you get a segmentation fault if your code tries to write past the stack.
在 Linux 上,如果您的代码试图越过堆栈写入,则会出现分段错误。
The size of the stack is a property inherited between processes. If you can read or modify it in the the shell using commands like ulimit -s
(in sh
, ksh
, zsh
) or limit stacksize
(tcsh
, zsh
).
堆栈的大小是进程间继承的属性。如果您可以使用ulimit -s
(in sh
, ksh
, zsh
) 或limit stacksize
( tcsh
, zsh
)等命令在 shell 中读取或修改它。
From a program, the size of the stack can be read using
从程序中,可以使用以下方法读取堆栈的大小
#include <sys/resource.h>
#include <stdio.h>
struct rlimit l;
getrlimit(RLIMIT_STACK, &l);
printf("stack_size = %d\n", l.rlim_cur);
I don't know of a standard way to get the size of the available stack.
我不知道获取可用堆栈大小的标准方法。
The stack starts with argc
followed by the contents of argv
and a copy of the environment, and then your variables. However because the kernel can randomize the location of the start of the stack, and there can be some dummy values above argc
, it would be wrong to assume that you have l.rlim_cur
bytes available below &argc
.
堆栈开始,argc
然后是环境的内容argv
和副本,然后是您的变量。然而,因为内核可以随机化堆栈开始的位置,并且上面可能有一些虚拟值argc
,所以假设l.rlim_cur
下面有可用字节是错误的&argc
。
One way to retrieve the exact location of the stack is to look at the file /proc/1234/maps
(where 1234
is the process ID of your program). Once you know these bounds you can compute how much of your stack is used by looking at the address of the latest local variable.
检索堆栈确切位置的一种方法是查看文件/proc/1234/maps
(1234
程序的进程 ID在哪里)。一旦知道了这些界限,就可以通过查看最新局部变量的地址来计算使用了多少堆栈。
回答by Kknd
gcc places an extra block of memory between the return address and the normal variables in "unsafe" function calls, like (in this example the function is void test() {char a[10]; b[20]}:
gcc 在“不安全”函数调用中的返回地址和普通变量之间放置了一个额外的内存块,例如(在本例中,函数是 void test() {char a[10]; b[20]}:
call stack:
-----------
return address
dummy
char b[10]
char a[20]
If the function write 36 bytes in the pointer 'a', the overflow will 'corrupt' the return address (possible security breach). But it will also change the value of the 'dummy', that is between the pointer and the return address, so the program will crash with a warning (you can disable this with a -fno-stack-protector)
如果函数在指针“a”中写入 36 个字节,溢出将“破坏”返回地址(可能存在安全漏洞)。但它也会改变“虚拟”的值,即指针和返回地址之间的值,因此程序将因警告而崩溃(您可以使用 -fno-stack-protector 禁用它)
回答by Norman Ramsey
On Linux, the Gnu libsigsegvlibraryincludes the function stackoverflow_install_handler
, which can detect (and in some cases help you recover from) stack overflow.
在 Linux 上,Gnu libsigsegv库包含函数stackoverflow_install_handler
,该函数可以检测(并在某些情况下帮助您恢复)堆栈溢出。
回答by deemok
On windows, the stack (for specific thread) grows on-demand until the stack size specified for this thread prior to its creation has been reached.
在 Windows 上,堆栈(对于特定线程)按需增长,直到达到创建之前为此线程指定的堆栈大小。
On-demand growing is impelmented using guard pages, in that there's a only a fragment of stack available initially, followed by a guard page, which, when hit, will trigger an exception - this exception is special, and is handled by the system for you - the handling increases the available stack space (also checked if a limit has been reached!) and the read operation is retried.
按需增长是使用保护页来实现的,因为最初只有一个可用的堆栈片段,然后是一个保护页,当它被命中时,将触发一个异常——这个异常是特殊的,由系统处理你 - 处理增加了可用的堆栈空间(还检查是否已达到限制!)并重试读取操作。
Once the limit is reached, there's no more growing which results in stack overflow exception.
The current stack base and limit are stored in thread environment block, in a struct called _NT_TIB
(thread information block).
If you have a debugger handy, this is what you see:
一旦达到限制,就不会再增长导致堆栈溢出异常。当前堆栈基数和限制存储在线程环境块中,在称为_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
The StackLimit attribute will get updated on-demand. If you check the attributes on this memory block, you'll see something similar to that:
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
And checking a page next to it reveals the guard attribute:
并检查它旁边的页面会显示 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
Hope it helps.
希望能帮助到你。
回答by Rob Walker
Stack overflow is probably the nastiest type of exception to handle -- because your exception handler has to deal with a minimal amount of stack (usually only a single page is reserved for this purpose).
堆栈溢出可能是最难处理的异常类型——因为您的异常处理程序必须处理最少量的堆栈(通常为此目的只保留一个页面)。
For an interesting discussion of the difficulties handling this type of exception see these blog posts: 1and 2from Chris Brumme which focus on the issue from the .NET perspective, particularly hosting the CLR.
有关处理此类异常的困难的有趣讨论,请参阅以下博客文章:Chris Brumme 的1和2,它们从 .NET 的角度关注该问题,尤其是托管 CLR。
回答by Richard
It is possible to use editbin in Visual Studio to change the stack size. The information can be found at msdn.microsoft.com/en-us/library/35yc2tc3.aspx.
可以在 Visual Studio 中使用 editbin 来更改堆栈大小。该信息可以在msdn.microsoft.com/en-us/library/35yc2tc3.aspx上找到。
回答by Shyam Sunder Verma
I would suggest you to use alternate-signal-stack if you are on linux.
如果您使用的是 linux,我建议您使用备用信号堆栈。
- In this case all the signal will be handled over alternate stack.
- In case stack overflow occurs, system generates a SEGV signal, this can be handled over alternate stack.
- If you do not use it ... then you may not be able to handle the signal, and your program may crash without any handling/erro-reporting.
- 在这种情况下,所有信号都将通过交替堆栈进行处理。
- 如果发生堆栈溢出,系统会生成 SEGV 信号,这可以通过备用堆栈进行处理。
- 如果您不使用它......那么您可能无法处理信号,并且您的程序可能会在没有任何处理/错误报告的情况下崩溃。
回答by dmityugov
Some compilers support stackavail() function, which returns the amount of remaining free space of the stack. You can use this function before calling functions in your programs that require a lot of stack space, to determine if it is safe to call them
一些编译器支持 stackavail() 函数,它返回堆栈的剩余可用空间量。您可以在调用需要大量堆栈空间的程序中的函数之前使用此函数,以确定调用它们是否安全