43

是否有一种标准方法可以查看您的应用程序有多少堆栈空间以及在运行期间堆栈使用的最高水印是多少?

同样在实际溢出的可怕情况下会发生什么?

它会崩溃、触发异常或信号吗?所有系统和编译器都有标准还是不同?

我正在专门寻找 Windows、Linux 和 Macintosh。

4

9 回答 9

16

Windows上会产生堆栈溢出异常。

下面的 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");
  }
}

运行此 exe 时,将生成以下输出:

Esp: 12FC4C
Esp: 12F96C
Esp: 12F68C
.....
Esp: 33D8C
Esp: 33AAC
Esp: 337CC

****** ExceptionFilter fired ******
于 2008-10-14T02:21:29.303 回答
15

在 Linux 上,如果您的代码尝试写入堆栈,则会出现分段错误。

堆栈的大小是进程之间继承的属性。ulimit -s如果您可以使用(in sh, ksh, zsh) 或limit stacksize( tcsh, )等命令在 shell 中读取或修改它zsh

从程序中,可以使用以下命令读取堆栈的大小

#include <sys/resource.h>
#include <stdio.h>

int main() {
    struct rlimit l;
    getrlimit(RLIMIT_STACK, &l);
    printf("stack_size = %ld\n", l.rlim_cur);
    return 0;
}

我不知道获取可用堆栈大小的标准方法。

堆栈以环境argc的内容argv和副本开头,然后是您的变量。但是,由于内核可以随机化堆栈开始的位置,并且上面可能有一些虚拟值argc,因此假设您在l.rlim_cur下面有可用的字节是错误的&argc

检索堆栈的确切位置的一种方法是查看文件/proc/1234/maps1234程序的进程 ID 在哪里)。一旦你知道了这些界限,你就可以通过查看最新的局部变量的地址来计算你的堆栈使用了多少。

于 2008-10-14T14:22:26.900 回答
10

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 禁用它)

于 2008-10-14T02:23:38.363 回答
7

在 Linux 上,Gnu libsigsegv包含函数stackoverflow_install_handler,它可以检测(并且在某些情况下帮助您从中恢复)堆栈溢出。

于 2009-10-17T04:02:59.040 回答
6

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.

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:

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:

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:

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.

于 2008-12-19T08:46:13.073 回答
4

堆栈溢出可能是最难处理的异常类型——因为您的异常处理程序必须处理最少量的堆栈(通常为此目的只保留一个页面)。

有关处理此类异常的困难的有趣讨论,请参阅以下博客文章: Chris Brumme 的12,它们从 .NET 角度关注该问题,特别是托管 CLR。

于 2008-10-14T02:36:57.823 回答
1

如果您在 linux 上,我建议您使用备用信号堆栈。

  1. 在这种情况下,所有信号都将通过备用堆栈进行处理。
  2. 如果发生堆栈溢出,系统会生成一个 SEGV 信号,这可以通过备用堆栈进行处理。
  3. 如果您不使用它......那么您可能无法处理信号,并且您的程序可能会在没有任何处理/错误报告的情况下崩溃。
于 2008-12-19T07:35:54.057 回答
1

一些编译器支持 stackavail() 函数,它返回堆栈的剩余可用空间量。您可以在调用程序中需要大量堆栈空间的函数之前使用此函数,以确定调用它们是否安全

于 2008-10-14T08:21:21.683 回答
1

可以在 Visual Studio 中使用 editbin 来更改堆栈大小。该信息可在msdn.microsoft.com/en-us/library/35yc2tc3.aspx中找到。

于 2011-08-11T10:26:27.827 回答