xjsXjtu
xjsXjtu

Reputation: 161

Why does not stack overflow/underflow trigger an run-time error?

I use this code snippet:

// stackoverflow.c 
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(int argc, char** argv)
{
    int i;
    int a[10];
    // init
    a[-1] = -1;
    a[11] = 11;
    printf(" a[-1]= = %d, a[11] = %d\n", a[-1], a[11]);
    printf("I am finished.\n");
    return a[-1];
}

The compiler is GCC for linux x86. It works well without any run-time error. I also test this code in Valgrind, which don't trigger any memory error either.

$ gcc -O0 -g -o stack_overflow stack_overflow.c
$ ./stack_overflow
   a[-1]= = -1, a[11] = 11
   I am finished.

$ valgrind ./stack_overflow
==3705== Memcheck, a memory error detector
==3705== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==3705== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==3705== Command: ./stack_overflow
==3705==
   a[-1]= = -1, a[11] = 11
   I am finished.
==3705==
==3705== HEAP SUMMARY:
==3705==     in use at exit: 0 bytes in 0 blocks
==3705==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==3705==
==3705== All heap blocks were freed -- no leaks are possible
==3705==
==3705== For counts of detected and suppressed errors, rerun with: -v
==3705== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

From my understanding, heap and stack is the same kind of memory. The only difference is that they grow in the opposite direction.

So my question is:

Why heap overflow/underflow will trigger an rum-time error, while stack overflow/underflow will not?

why C language designer didn't take this into account just like heap, other than leave it Undefined Behaviour

Upvotes: 1

Views: 4226

Answers (6)

Jennifer
Jennifer

Reputation: 153

Allocating heap storage always includes a test for insufficient memory; for stack space this is less critical due to the way stack space is reused over and over again. If they share the same block of storage, then they could collide.

GCC won't do this because heap space and stack space are separate; I don't know about Valgrind.

In at least one old language (Turbo C), an alloc() will fail if less than 256 bytes of storage remain between top-of-heap and bottom-of-stack. It is assumed 256 bytes is enough to accommodate stack growth. If it's not, you get some very weird run-time errors.

Turbo C has a compile-time option, -N, to check for stack overflow more thoroughly. Other languages may have a similar option.

Upvotes: 0

chux
chux

Reputation: 153456

Why does not stack overflow/underflow trigger an run-time error?

C is not limited to "heap" and "stack" implementations. Example: Variables in main() need not be in a "stack". Even GCC may optimize in way that defy a simple understanding. Many memory architectures are possible. Since C does not specify the underlying memory architecture, the following is simply undefined behavior. @Karoly Horvath

// Undefined behavior: accessing memory outside array's range.
int a[10];
a[-1] = -1;
a[11] = 11;

Any analysis may make sense with a given memory model on a given day of the week, but that behavior is just one of many possibilities.

Upvotes: 0

4566976
4566976

Reputation: 2499

valgrind does not detect stack buffer overflows. Use AddressSanitizer. At least gcc 4.8 is required and libasan must be installed.

gcc -g -fsanitize=address stackbufferoverflow.c

==1955==ERROR: AddressSanitizer: stack-buffer-underflow on address 0x7fffff438d4c at pc 0x000000400a1d bp 0x7fffff438d10 sp 0x7fffff438d00
WRITE of size 4 at 0x7fffff438d4c thread T0
    #0 0x400a1c in main /home/m/stackbufferoverflow.c:9
    #1 0x7fe7e24e178f in __libc_start_main (/lib64/libc.so.6+0x2078f)
    #2 0x400888 in _start (/home/m/a.out+0x400888)

Address 0x7fffff438d4c is located in stack of thread T0 at offset 28 in frame
    #0 0x400965 in main /home/m/stackbufferoverflow.c:5

  This frame has 1 object(s):
    [32, 72) 'a' <== Memory access at offset 28 underflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-underflow /home/m/stackbufferoverflow.c:9 main

Upvotes: 4

Maresh
Maresh

Reputation: 4712

EDIT

Here's an interesting tuto:

http://gribblelab.org/CBootcamp/7_Memory_Stack_vs_Heap.html

BTW Clang (OSX) detects it, but it's just and extra feature, good old gcc would let you do it.

ctest.c:6:5: warning: array index 42 is past the end of the array (which contains 1 element) [-Warray-bounds]
    a[42] = 42;
    ^ ~~
cpp.cpp:4:5: note: array 'a' declared here
    int a[1];
    ^
1 warning generated.

Old

a[11] = 11;

Would trigger a Segmentation fault (but here it's only one byte it's just overriding the value of another variable, most likely), if you want a stack overflow try something that does an infinite recursion.

Also if you want to make your code segfault proof (for malloc only) I suggest you compile it with electric fence for your tests. It will prevent your program to go above its allocated memory (starting from the first byte)

http://linux.die.net/man/3/efence

As suggested in the comments Valgrind is also a useful tool.

http://valgrind.org/

Upvotes: 0

Thomas Padron-McCarthy
Thomas Padron-McCarthy

Reputation: 27632

C doesn't check things like out-of-bounds array indexing. It just does what you told it to, in this case to change element number 11 in an array of 10 elements. Typically, this means that your program writes to the location in memory where this item should have been stored, if it had existed. This may or may not cause some sort of visible error, such a crash. It might have no effect, or it could make your program do something strange. It depends on what, if anything, happened to be stored at that place in memory, and how it is used.

Some other programming languages do perform checks such as these, and guarantee that an error will be reported. The C standard gives no such guarantees, and just says that it will cause "undefined behaviour". One reason for this is that it should be possible to write very efficient programs in C, where checks would cause a small, but in some cases perhaps unacceptable, delay. Also, back when C was designed, computers were slower, and the delay would have been a worse problem.

There is also no guarantee in C that heap errors will be detected or reported. Valgrind is not part of the C language, but a different tool, and it does its best to find errors using other and more effective mechanisms than C would, but there is no guarantee that it will find all errors.

Upvotes: 0

dlask
dlask

Reputation: 8982

why C language designer didn't take this into account just like heap, other than leave it Undefined Behaviour

The original C langauge designers wrote a kind of more comfortable and portable assembler for themselves. The original language has not been designed to be bullet-proof against programmers' errors.

If you are interested in an opposite example then look at Ada (http://en.wikipedia.org/wiki/Ada_%28programming_language%29).

Upvotes: 0

Related Questions