skip to Main Content

I’m trying to trigger a stack-overflow crash.

most probably, the crash value is not 1684 in you sandbox. but you will find your own value when running.

I just want to know a way to figure these out.

The program is compiled in CentOS 8, using a GNU compiler.

#include <stdio.h>

int main() {

        int a[3];

        for(int i=4;i<100000;i++){
                printf("a[%d]=%dn", i, a[i]);
                a[i] = 0;
        }
        return 0;

}

Then 1864 will be the first I value which will cause a crash.

I known stack-overflow will cause undefined behavior.
I just want to know the memory structure of this process. why a[1864] will cause a crash.

3

Answers


  1. why a[1864] will cause crash.

    This particular value is by no means guaranteed or reliable. It will depend on the compiler and libc version, compilation flags, and a host of other variables.

    In general, the memory layout looks like this (on machines where stack grows down, which is the majority of current machines):

              <- inaccessible memory
    0x...C000 <- top of stack (page-aligned)
    0x...B... <- stack frame for _start
    ...       <- other stack frames, such as __libc_start_main
    0x...A... <- stack frame for main (where a[] is located).
    

    You start overwriting stack from &a[0], and continue going until you hit the top of the stack and step into inaccessible memory. The number of ints between &a[0] and top of stack depends on the factors I listed.

    Login or Signup to reply.
  2. You crash when you access an address that’s not mapped into your address space, which happens when you run off the top of the stack.

    You can get some insight by looking at the address of a as well as your program’s memory mappings, which you can see by reading /proc/self/maps from within your program. See https://godbolt.org/z/1e5bvK for an example. In my test, a is 0x7ffcd39df7a0, and the stack is mapped into the address range 7ffcd39c0000-7ffcd39e1000, beyond which is a gap of unmapped addresses. Subtracting, a is 6240 bytes from the top of the stack. Each int is 4 bytes, so that’s 1560 ints. Thus accessing a[1560] should crash, and that’s exactly what happened.

    The addresses and offsets will change from run to run due to ASLR and variation in how the startup code uses the stack.

    (Just to be clear for other readers: accessing beyond the end of the stack is what will cause an immediate segfault. But as soon as you write even one element beyond the declared size of your array, even though that write instruction may not itself segfault, it is still overwriting other data that is potentially important. That is very likely to cause some sort of misbehavior further along, maybe very soon or maybe much later in your program’s execution. The result could eventually be a segfault, if you overwrote a pointer or a return address or something of the kind, or it could be something worse: data corruption, granting access to an intruder, blowing up the machine you’re controlling, becoming Skynet, etc.)

    Login or Signup to reply.
  3. Using a powerful debugging tool, I was able to find the source of your troubles.

    lstrand@styx:~/tmp$ insure gcc -g -o simple simple.c
    lstrand@styx:~/tmp$ ./simple >/dev/null
    [simple.c:8] **READ_BAD_INDEX**
    >>                 printf("a[%d]=%dn", i, a[i]);
    
      Reading array out of range: a[i]
    
      Index used : 4
    
      Valid range: 0 thru 2 (inclusive)
    
      Stack trace where the error occurred:
                                main()  simple.c, 8
    
    [simple.c:9] **WRITE_BAD_INDEX**
    >>                 a[i] = 0;
    
      Writing array out of range: a[i]
    
      Index used : 4
    
      Valid range: 0 thru 2 (inclusive)
    
      Stack trace where the error occurred:
                                main()  simple.c, 9
    
    **Memory corrupted.  Program may crash!!**
    
    **Insure trapped signal: 11**
    
      Stack trace where the error occurred:
                                main()  simple.c, 8
    Segmentation violation
    ** TCA log data will be merged with tca.log **
    Segmentation fault (core dumped)
    lstrand@styx:~/tmp$ 
    

    Edit:

    To clarify, your program seems to assume that you can grow an array in C/C++ simply by writing past the end, as in JavaScript. There is also the subtle suggestion that you are coming from a Fortran background. The first past-the-end array location is at index 3, not 4.

    In other words, your test program declares a fixed-size array on the stack. An array of size 3: with valid elements a[0], a[1], and a[2]. At the very first iteration of the loop, you are corrupting the stack.

    The proper way to cause a stack overflow, as people on this site should well know, is to do something like this:

    void recurse()
    {
        char buf[1024];
        recurse();
    }
    
    int main()
    {
        recurse();
        return 0;
    }
    

    On Linux/x86_64, this still produces SIGSEGV when you run out of stack, but on other platforms (e.g., Windows) you will get a stack overflow violation.

    (gdb) r
    Starting program: /home/lstrand/tmp/so 
    
    Program received signal SIGSEGV, Segmentation fault.
    0x0000000000400df2 in recurse () at so.c:5
    5       recurse();
    (gdb) where
    #0  0x0000000000400df2 in recurse () at so.c:5
    #1  0x0000000000400e79 in recurse () at so.c:5
    #2  0x0000000000400e79 in recurse () at so.c:5
    #3  0x0000000000400e79 in recurse () at so.c:5
    [...]
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search