How to detect possible / potential stack overflow problems in a c / c++ program?

前端 未结 9 1836
滥情空心
滥情空心 2020-12-04 21:55

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 o

相关标签:
9条回答
  • 2020-12-04 22:14

    On Linux you get a segmentation fault if your code tries to write past the stack.

    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).

    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.

    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.

    0 讨论(0)
  • 2020-12-04 22:17

    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]}:

    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)

    0 讨论(0)
  • 2020-12-04 22:24

    On Windows a stack overflow exception will be generated.

    The following windows code illustrates this:

    #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:

    Esp: 12FC4C
    Esp: 12F96C
    Esp: 12F68C
    .....
    Esp: 33D8C
    Esp: 33AAC
    Esp: 337CC
    
    ****** ExceptionFilter fired ******
    
    0 讨论(0)
  • 2020-12-04 22:24

    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: 1 and 2 from Chris Brumme which focus on the issue from the .NET perspective, particularly hosting the CLR.

    0 讨论(0)
  • 2020-12-04 22:28

    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.

    0 讨论(0)
  • 2020-12-04 22:30

    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.

    0 讨论(0)
提交回复
热议问题