views:

126

answers:

4

Hi, could someone please teach me a common example whereby (!) you destroy the stack in a C program? I use GCC, in Ubuntu. Thanks.

A: 

You can't, at least, not according to the C standard. You might be able to use inline assembler features of GCC to mess with the stack pointer though.

EDIT: I suppose calling exit, abort, or terminate (terminate is in C++ only) would result in destruction of the stack :P

Billy ONeal
I really wish someone would have given a comment with respect to why they downvoted this answer....
Billy ONeal
I don't know why they downvoted your answer, but it sure isn't helpful to me. I don't think you understood the question.
Dervin Thunk
A: 

I'm not sure exactly what you mean, but whenever you exit a function the "stack" for that function is destroyed. For example:

void foo(void) {
    // a, b and c are "allocated" on the stack here
    int a, b, c;

}    // a, b and c are destroyed here

In reality the stack is never destroyed in the way you might be thinking. There is a pointer to the top of the stack and functions reference positions relative the current top of stack. When a function exits the TOS pointer is decremented by a certain amount, but no actual destruction takes place. So you could theoretically still access the values from the function after it exits, although that would be a bad idea.

You might want to take a look at these:

How Does The Function Call Stack Work?

Calling Conventions in C and C++

Robert S. Barnes
"TOS" pointer? I'm not aware of an architecture with such a thing. At least on x86, the register you're looking for is `ESP`. This is the problem with answering with one particular architecture's behavior when talking about the C language as specified in the standard.
Billy ONeal
@Billy ONeal: TOS == Top of Stack, probably ESP on x86 as you point out.
Robert S. Barnes
+5  A: 

It depends on what you mean by "destroy the stack", but this is a common error that will generally cause corruption of vital stack-resident data:

void dumb()
{
    char small[2];
    strcpy(small, "too long to fit"); // Writes past the end of "small", overwriting vital information
}

This is a common source of security holes. It can potentially be used to hijack the instruction pointer, enabling malicious code to execute. See buffer overflow.

Another mistake that might be described as "destroying the stack" is the case of infinite recursion (scroll down on that page):

int add(int n)
{
    return n + add(n + 1);
}

Which, as it lacks an exit condition, will push so many frames onto the stack that it eventually gets "full". (Unless the recursion is optimized out; see below)

Both of these examples compile without as much as a warning using GCC 4.4.3.


Note: As Billy ONeal pointed out below, the behavior of these examples is specific to x86, not C as a language, and may vary from one compiler to another. That is not to say that they don't demonstrate how you can break the stack in a particular (and extremely common) implementation of C.

Martin Törnwall
Note: All of this is specific to x86. Nothing here is specified by the C standard. (C doesn't know about SIGSEGV/EXCEPTION_ACCESS_VIOLATION, nor does it know of any sort of virtual memory model)
Billy ONeal
Yep, just realized it myself. I'll update my post accordingly. Thanks for pointing that out.
Martin Törnwall
@Martin: +1 for edit. One other thing of note: If the compiler tailcall optimizes the `add` function, it might simply loop forever rather than crashing, even on x86.
Billy ONeal
@Billy ONeal: yes -- confirmed! No crash when compiling my example with -O3. Interesting. +1
Martin Törnwall
@Billy ONeal: it can't simply tailcall optimize `add`, although it could replace it with a loop; introduce an accumulator parameter; and/or spot that the function cannot possibly return, and replace it with something that doesn't do any addition at all. And while you say that the results are specific to x86, you might just as well say they're specific to ARM, specific to MIPS, specific to PowerPC, and so on. A stack features in quite a few C ABIs, and where there's a stack, these programs will tend to break it...
Steve Jessop
@Steve: The fact that these programs are undefined behavior that usually results in stack corruption I'm not really challenging here. I'm more challenging the statement "generally causing a segfault upon return" -- because "segfault" is an artifact of the x86 memory model.
Billy ONeal
@Steve Jessop: after reading the disassembly, I can conclude that add basically gets optimized to jmp $
Martin Törnwall
@Billy: I agree with you on "segfault" being an x86 artifact. I've edited the first paragraph slightly to be less specific to that architecture.
Martin Törnwall
If I wrote a C compiler, it would compile your add function to `1: jmp 1`.
R..
+2  A: 

Here are a few more examples where the stack can get trashed.

char* foo()
{
    char str[256];
    return str;
}

void bar()
{
    char* str = foo();
    strcpy(str, "Holy sweet Moses! I blew my stack!!");
}

Or,

void foo()
{
    char* str; // uninitialized; has garbage value
    strcpy(str, "Holy sweet Moses! I blew my stack!!");
    // well, could be anything you are trashing
}

void foo()
{
    int* ptr; // uninitialized; has garbage value
    *ptr = "0xDEADBEEF";
    // well, could be anything you are trashing
}
Ziffusion
Okay, the first example works. But the second example has nothing to do with the stack. Just because it's a stack allocated pointer doesn't mean it has anything to do with the stack.
Billy ONeal