Is there a defined behavior for handling a stack overflow?
Apart from terminating the process, it doesn't seem like there's a whole lot that can be done. I'm just wondering if anyone might know what the C standard has to say about it.
Is there a defined behavior for handling a stack overflow?
Apart from terminating the process, it doesn't seem like there's a whole lot that can be done. I'm just wondering if anyone might know what the C standard has to say about it.
The standard does not require the use of a stack, and has nothing to say about stack overflows.
The C99 standard doesn't define a stack; it only discusses automatic or allocated storage in the abstract, whereas a contiguous stack with overflow detection is only one mechanism for implementing automatic storage.
Section 7.14 of the standard defines SIGSEGV as the signal which occurs on "an invalid access to storage". Implementations of C are not required to generate any signals, but implementations using a contiguous fixed size stack* typically signal SIGSEGV if a stack overflow is detected.
You can register a signal handler function for SIGSEGV, but it can't return - "[i]f and when the function returns, if the value of sig is SIGFPE, SIGILL, SIGSEGV, or any other implementation-defined value corresponding to a computational exception, the behavio[u]r is undefined".
(* not that I've knowingly worked with C implementations which don't, but I'm not aware of anything in the C standard preventing the use of common techniques used to implement growable automatic storage domains in other environments)
The C standard doesn't even define a stack, so it certainly doesn't define what happens when the stack overflows.
The answers here are correct in stating that it is nothing to do with the c standard but your statement that "Apart from terminating the process, it doesn't seem like there's a whole lot that can be done" is not - as a generality - true.
In fact, on most systems with virtual memory management and on demand paging, the allocated stack is quite small (typically 4KB more than is currently being used) and often overflows (which generates a page fault interrupt) and the OS simply adds another page of memory to the stack for the thread.
The stack limit of - typically - 1MB, is just a fairly arbitrary figure chosen to guard against runaway programs and generally isn't an absolute limit (though it was on some memory models with intel processors IIRC). It would generally not make sense to allocate 1MB of physical memory to each thread.
According to some answers to this question, the C standards don't even have anything to say about the existence of a stack, let alone a stack overflow.
I'm fairly sure the specifics of what happen are defined by the operating system however, in all cases, the program should exit. You are correct in assuming that there is really nothing you can do once a stack overflow occurs (at least as a programmer). All you can really do is prevent them from happening in the first place.
It's up to the operating system. Many operating systems allow the default stack size to be overwritten, though. For example, on Windows, you can use this linker flag to increase the stack size from 1MB to a higher value.
As other people have mentioned, the standard does not say anything about a stack.
But, I think it it were to define stack overflow behavior, the standard would say it results in undefined behavior.
::rimshot::
On some systems, ensuring any kind of predictable favor after a stack overflow would add considerable overhead to every function call; thus, the standard quite reasonably regards stack overflow as Undefined Behavior. This is entirely appropriate if the goal is to maximize the efficiency with which an implementation can run legitimate programs.
The standard also doesn't require that systems have enough stack to support any non-trivial depth of function calls. Given that some useful programs (especially in the embedded-systems world) can get by with less than 16 bytes of stack, and may not necessarily be able to spare any more RAM than that, requiring a generous stack would violate the philosophy of "don't pay for what you don't need".
Unfortunately, the fact that there's no way for a program to say anything about what sort of depth it will require, nor query what sort of stack is available, the only programs which can be guaranteed not to engage in Undefined Behavior are those whose stack usage is below the minimum guarantees; outside of the embedded systems world, that basically means that the standard guarantees nothing about any program which is larger than a toy.
The C standard is contradictory in this regard. Consider the following program:
void foo(uintptr_t n)
{
int a;
printf("%p\n", (void *)&a);
if (n+1) foo(n+1);
}
int main()
{
int a;
printf("%p\n", (void *)&a);
foo(0);
}
This program is perfectly conforming and does not violate any of the minimum translation limits, and as others have said, there is nothing in the language of the standard about stack limits/overflow. However, it produces UINTPTR_MAX
+2 objects a
(at each call level), whose lifetimes all overlap, each with distinct addresses. This is impossible by a mere counting argument.