How to Handle or Avoid a Stack Overflow in C++

How does a stack overflow occur and how do you prevent it?

Stack

A stack, in this context, is the last in, first out buffer you place data while your program runs. Last in, first out (LIFO) means that the last thing you put in is always the first thing you get back out - if you push 2 items on the stack, 'A' and then 'B', then the first thing you pop off the stack will be 'B', and the next thing is 'A'.

When you call a function in your code, the next instruction after the function call is stored on the stack, and any storage space that might be overwritten by the function call. The function you call might use up more stack for its own local variables. When it's done, it frees up the local variable stack space it used, then returns to the previous function.

Stack overflow

A stack overflow is when you've used up more memory for the stack than your program was supposed to use. In embedded systems you might only have 256 bytes for the stack, and if each function takes up 32 bytes then you can only have function calls 8 deep - function 1 calls function 2 who calls function 3 who calls function 4 .... who calls function 8 who calls function 9, but function 9 overwrites memory outside the stack. This might overwrite memory, code, etc.

Many programmers make this mistake by calling function A that then calls function B, that then calls function C, that then calls function A. It might work most of the time, but just once the wrong input will cause it to go in that circle forever until the computer recognizes that the stack is overblown.

Recursive functions are also a cause for this, but if you're writing recursively (ie, your function calls itself) then you need to be aware of this and use static/global variables to prevent infinite recursion.

Generally, the OS and the programming language you're using manage the stack, and it's out of your hands. You should look at your call graph (a tree structure that shows from your main what each function calls) to see how deep your function calls go, and to detect cycles and recursion that are not intended. Intentional cycles and recursion need to be artificially checked to error out if they call each other too many times.

Beyond good programming practices, static and dynamic testing, there's not much you can do on these high level systems.

Embedded systems

In the embedded world, especially in high reliability code (automotive, aircraft, space) you do extensive code reviews and checking, but you also do the following:

  • Disallow recursion and cycles - enforced by policy and testing
  • Keep code and stack far apart (code in flash, stack in RAM, and never the twain shall meet)
  • Place guard bands around the stack - empty area of memory that you fill with a magic number (usually a software interrupt instruction, but there are many options here), and hundreds or thousands of times a second you look at the guard bands to make sure they haven't been overwritten.
  • Use memory protection (ie, no execute on the stack, no read or write just outside the stack)
  • Interrupts don't call secondary functions - they set flags, copy data, and let the application take care of processing it (otherwise you might get 8 deep in your function call tree, have an interrupt, and then go out another few functions inside the interrupt, causing the blowout). You have several call trees - one for the main processes, and one for each interrupt. If your interrupts can interrupt each other... well, there be dragons...

High-level languages and systems

But in high level languages run on operating systems:

  • Reduce your local variable storage (local variables are stored on the stack - although compilers are pretty smart about this and will sometimes put big locals on the heap if your call tree is shallow)
  • Avoid or strictly limit recursion
  • Don't break your programs up too far into smaller and smaller functions - even without counting local variables each function call consumes as much as 64 bytes on the stack (32 bit processor, saving half the CPU registers, flags, etc)
  • Keep your call tree shallow (similar to the above statement)

Web servers

It depends on the 'sandbox' you have whether you can control or even see the stack. Chances are good you can treat web servers as you would any other high level language and operating system - it's largely out of your hands, but check the language and server stack you're using. It is possible to blow the stack on your SQL server, for instance.

-Adam

How to handle or avoid a stack overflow in C++

Handling a stack overflow is not the right solution, instead, you must ensure that your program does not overflow the stack.

Do not allocate large variables on the stack (where what is "large" depends on the program). Ensure that any recursive algorithm terminates after a known maximum depth. If a recursive algorithm may recurse an unknown number of times or a large number of times, either manage the recursion yourself (by maintaining your own dynamically allocated stack) or transform the recursive algorithm into an equivalent iterative algorithm

A program that must be "really robust" will not use third-party or external libraries that "eat a lot of stack."


Note that some platforms do notify a program when a stack overflow occurs and allow the program to handle the error. On Windows, for example, an exception is thrown. This exception is not a C++ exception, though, it is an asynchronous exception. Whereas a C++ exception can only be thrown by a throw statement, an asynchronous exception may be thrown at any time during the execution of a program. This is expected, though, because a stack overflow can occur at any time: any function call or stack allocation may overflow the stack.

The problem is that a stack overflow may cause an asynchronous exception to be thrown even from code that is not expected to throw any exceptions (e.g., from functions marked noexcept or throw() in C++). So, even if you do handle this exception somehow, you have no way of knowing that your program is in a safe state. Therefore, the best way to handle an asynchronous exception is not to handle it at all(*). If one is thrown, it means the program contains a bug.

Other platforms may have similar methods for "handling" a stack overflow error, but any such methods are likely to suffer from the same problem: code that is expected not to cause an error may cause an error.

(*) There are a few very rare exceptions.

Generally, How do I prevent integer overflow from happening in C language?

Whenever you declare an integer variable:

  1. Actually consider how large/small a number it will ever contain.
  2. Actually consider if it needs to be signed or unsigned. Unsigned is usually less problematic.
  3. Pick the smallest type of intn_t or uintn_t types from stdint.h that will satisfy the above (or the ...fast_t etc flavours if you wish).
  4. If needed, come up with integer constants that contain the maximum and/or minimum value the variable will hold and check against those whenever you do arithmetic.

That is, don't just aimlessly spam int all over your code without a thought.

Signed types can be problematic for other reasons than overflow too, namely whenever you need to do bitwise arithmetic. To avoid over/underflow and accidental signed bitwise arithmetic, you also need to know of the various implicit integer type promotion rules.



Is integer overflow going to get me hacked like buffer overflow or etc?

Not really, but any bug can of course be exploited if someone is aware of it - as you can see in almost every single computer game.

How do I prevent and/or handle a StackOverflowException?

From Microsoft:

Starting with the .NET Framework
version 2.0, a StackOverflowException
object cannot be caught by a try-catch
block and the corresponding process is
terminated by default. Consequently,
users are advised to write their code
to detect and prevent a stack
overflow. For example, if your
application depends on recursion, use
a counter or a state condition to
terminate the recursive loop.

I'm assuming the exception is happening within an internal .NET method, and not in your code.

You can do a couple things.

  • Write code that checks the xsl for infinite recursion and notifies the user prior to applying a transform (Ugh).
  • Load the XslTransform code into a separate process (Hacky, but less work).

You can use the Process class to load the assembly that will apply the transform into a separate process, and alert the user of the failure if it dies, without killing your main app.

EDIT: I just tested, here is how to do it:

MainProcess:

// This is just an example, obviously you'll want to pass args to this.
Process p1 = new Process();
p1.StartInfo.FileName = "ApplyTransform.exe";
p1.StartInfo.UseShellExecute = false;
p1.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

p1.Start();
p1.WaitForExit();

if (p1.ExitCode == 1)
Console.WriteLine("StackOverflow was thrown");

ApplyTransform Process:

class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
throw new StackOverflowException();
}

// We trap this, we can't save the process,
// but we can prevent the "ILLEGAL OPERATION" window
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.IsTerminating)
{
Environment.Exit(1);
}
}
}

Meaning of a stack overflow in C programming

which memory is it?

It is the stack memory, namely that which the program uses to store information during a function call about where to return to. Each time you call a function, the CPU saves the location of what's currently executing onto the stack, then jumps to the new function. When the function is done, it pops that location off the stack and returns there. This is what makes recursion possible.

What is the size of stack that is overflowing?

The actual size of the stack is completely platform-dependent. Many operating systems tend to limit the size of the stack to a few megabytes, but allow that size to be changed. If the system runs out of virtual memory, this can also limit the stack size. Some (much) older CPUs have a fixed-size stack (the Intel 8008 only has 7 stack entries).

Catching stack overflow

Off the top of my head, one way to catch excessive stack growth is to check the relative difference in addresses of stack frames:

#define MAX_ROOM    (64*1024*1024UL)    // 64 MB

static char * first_stack = NULL;

void foo(...args...)
{
char stack;

// Compare addresses of stack frames
if (first_stack == NULL)
first_stack = &stack;
if (first_stack > &stack && first_stack - &stack > MAX_ROOM ||
&stack > first_stack && &stack - first_stack > MAX_ROOM)
printf("Stack is larger than %lu\n", (unsigned long)MAX_ROOM);

...code that recursively calls foo()...
}

This compares the address of the first stack frame for foo() to the current stack frame address, and if the difference exceeds MAX_ROOM it writes a message.

This assumes that you're on an architecture that uses a linear always-grows-down or always-grows-up stack, of course.

You don't have to do this check in every function, but often enough that excessively large stack growth is caught before you hit the limit you've chosen.

Best way to handle Integer overflow in C#?

I haven't needed to use this often, but you can use the checked keyword:

int x = foo();
int test = checked(x * common);

Will result in a runtime exception if overflows. From MSDN:

In a checked context, if an expression produces a value that is
outside the range of the destination type, the result depends on
whether the expression is constant or non-constant. Constant
expressions cause compile time errors, while non-constant expressions
are evaluated at run time and raise exceptions.

I should also point out that there is another C# keyword, unchecked, which of course does the opposite of checked and ignores overflows. You might wonder when you'd ever use unchecked since it appears to be the default behavior. Well, there is a C# compiler option that defines how expressions outside of checked and unchecked are handled: /checked. You can set it under the advanced build settings of your project.

If you have a lot of expressions that need to be checked, the simplest thing to do would actually be to set the /checked build option. Then any expression that overflows, unless wrapped in unchecked, would result in a runtime exception.

Getting a stack overflow exception when declaring a large array

Your array is way too big to fit into the stack, consider using the heap:

int *sieve = malloc(2000000 * sizeof(*sieve));

If you really want to change the stack size, take a look at this document.

Tip: - Don't forget to free your dynamically allocated memory when it's no-longer needed.



Related Topics



Leave a reply



Submit