======================= Cover sheet starts here ======================== Document Number: WG14 N___/X3J11 __-___ C9X Revision Proposal ===================== Title: Stack overflow handling Author: David R. Tribble Author Affiliation: Self Postal Address: ************** USA E-mail Address: david@tribble.com Web: http://david.tribble.com Telephone Number: *************** Fax Number: *************** Sponsor: ________________________________________ Revision: Revision 10, 2003-05-31 Supersedes: Revision 9, 1999-07-28 Proposal Category: __ Editorial change/non-normative contribution __ Correction X_ New feature __ Addition to obsolescent feature list __ Addition to Future Directions __ Other (please specify) _____________________________ Area of Standard Affected: __ Environment __ Language __ Preprocessor X_ Library X_ Macro/typedef/tag name X_ Function __ Header __ Other (please specify) _____________________________ Prior Art: None known. Target Audience: Programmers suffering from stack overflow Related Documents (if any): None Proposal Attached: X_ Yes __ No, but what's your interest? Abstract: The addition of library functions to allow for the detection of, and recovery from, stack shortage conditions. ======================= Cover sheet ends here ========================== DESCRIPTION This proposal describes a mechanism for detecting stack overflow during program execution, and allows the programmer to establish a point of control at which to catch such a stack shortage condition and resume normal execution. The mechanism described is based in part on the semantics of the existing 'setjmp()' and 'longjmp()' standard library functions. PROPOSAL The standard library header shall contain a declaration for the type struct __stackjmpbuf and declarations for the constants _STACKJMP_ERROR _STACKJMP_JUMP _STACKJMP_VERSION and declarations for the functions __stackjmp_get() __stackjmp_raise() __stackjmp_restore() __stackjmp_set() These items are described in detail below. [Rationale: It is thought that the global library namespace should be left as unpolluted as possible. To this end, the new names invented for these types, constants, and functions are prefixed with one or two leading underscores, which places them in the namespace reserved for the implementation.] [Discussion: Should these library items be declared in instead of ? It would make sense to group all of the functions dealing with non-local transfer of control into the same header.] TERMS A "stack shortage condition" is a state that occurs when an attempt is made to invoke a function or enter a block that requires more stack memory space (e.g., the space allocated for 'auto' variables, function parameter values, and program counters) than is available at that time. Such a condition can only occur when the stack is in a consistent, stable state. In particular, such a condition may occur during the execution of a standard library function. A "stable stack" is a state during the program execution in which the stack is in a consistent, stable state. A "stack shortage control object" is an object of '__stackjmpbuf' structure type which contains information necessary for handling stack shortage conditions. A program has only one such control object active (established) at any given time during its execution. TYPES 1. Structure __stackjmpbuf The '__stackjmpbuf' structure contains data necessary for the implementation of the semantics of the '__stackjmp_set()' library function. It contains the following member: void (*__sj_func)(void); // Pointer to the function causing // the stack shortage condition [Rationale: A function pointer of any type can be cast to a function pointer of type 'void (*)(void)' and back to its original type without any loss of information. The 'void (*)(void)' type is as close to a "void pointer" for function pointer types as we can get.] The structure may contain other members, which are implementation- defined. The following clauses describe the semantics of the structure members in further detail. __sj_func This points to the function to which the attempted invocation resulted in a stack shortage condition (suitably cast to type 'void (*)(void)'). If the implementation is not capable of determining this information, this member is a null pointer value. No other details about the size, type, or meaning of the contents of the '__stackjmpbuf' structure are defined in this [standard] proposal. [Rationale: This leaves a lot of leeway for implementations to put whatever control information they may require into the structure. It also allows implementations to define other members which contain more detailed information pertaining to the stack shortage condition.] CONSTANTS The following constant is of integer type: _STACKJMP_VERSION If the implementation supports the semantics of the __stackjmp_set() functions, this macro has a value equal to a long int value YYYYMML, which represents the date of the ISO standard that the implementation conforms to with regard to the _stackjmp() functions; otherwise the value is equal to 0. [Rationale: Programs can test the value of this macro to determine whether or not the implementation fully supports the stackjmp functions. For example: #if _STACKJMP_VERSION > 0 ... Code that calls __stackjmp_set() ... #endif This preprocessor test works even for implementations that do not define the _STACKJMP_VERSION macro at all. --end Rationale.] The following constants are of type 'pointer to struct __stackjmpbuf'. They have distinct non-null values that compare unequal to the address of any user-defined object in the executing program [note]. _STACKJMP_ERROR _STACKJMP_JUMP Note: This implies that they may be equal to the addresses of (otherwise hidden) objects in the standard runtime library, though they need not be. [Rationale: These constants are necessary to indicate special return values of the '__stackjmp_set()' function. As such, they must not compare equal to the address of any user-defined variable. Typical implementations of these constants could be as pointers to reserved objects in the runtime library, e.g.: extern struct __stackjmpbuf __stackjmp_err; extern struct __stackjmpbuf __stackjmp_jmp; #define _STACKJMP_ERROR (&__stackjmp_err) #define _STACKJMP_JUMP (&__stackjmp_jmp) Or perhaps: extern struct __stackjmpbuf *const _STACKJMP_ERROR; extern struct __stackjmpbuf *const _STACKJMP_JUMP; --end Rationale.] FUNCTIONS 1. Function __stackjmp_get() extern struct __stackjmpbuf * __stackjmp_get(void); The '__stackjmp_get()' function returns a pointer to the object that is currently established as the stack shortage control object; if no such object has been previously established (i.e., if no prior call was made to '__stackjmp_set()'), or if the previous call to the '__stackjmp_set()' function was passed a null 'info' parameter, the function returns null [note]. Note: A null pointer return value thus indicates that no stack shortage control object is currently established. A program may have only one established stack shortage control object, or none at all, at any time during its execution. It is unspecified whether this function is a preprocessor macro or an identifier declared with external linkage. If a macro definition is suppressed in order to access an actual function, or a program defines an external identifier with the same name, the behavior is undefined. If the implementation does not support the semantics of this function or if some other error occurs, this function returns a pointer value equal to '_STACKJMP_ERROR'. [Rationale: Implementations are strongly encouraged to support the semantics of the '__stackjmp_get()' function. It is recognized, however, that this might impose unacceptable performance constraints or simply prove impossible for some implementations. Therefore, implementations are not required to support these semantics; they are required, however, to provide this function.] 2. Function __stackjmp_raise() extern void __stackjmp_raise(const struct __stackjmpbuf *info); The '__stackjmp_raise()' function causes the program to behave as if a stack shortage condition has occurred, which in turn causes program control to return to the point of the last call to the '__stackjmp_set()' function, returning a value equal to '_STACKJMP_JUMP' as described in the '__stackjmp_set()' function. If the 'info' parameter is null, the information passed to the stack shortage handler is implementation-defined (i.e., the resulting value of the '__sj_func' member of the condition handler currently in effect is set to an implementation-defined value). If the 'info' parameter is not null, it points to an object containing member values that are to be passed to the currently active stack shortage control object; in particular, the value of the '__sj_func' member of the pointed-to structure is the value to which the '__sj_func' member of the active control object is set. (The implementation may provide other members of the structure whose values are passed to the stack shortage handler; such members and their semantics are implementation-defined.) [Discussion: Passing a null 'info' pointer in a typical implementation would most likely result in the raising of a stack shortage condition that appears to originate from the function that called '__stackjmp_raise()', i.e., the '__sj_func' member of the currently active control object would be set to point to the calling function.] [Discussion: The 'info' object can be filled with values prior to raising a stack shortage condition. Since the '__sj_func' is the only member defined by this standard, it is the only member value guaranteed to be passed to the handler. However, we give implementations the latitude of defining semantics for other members that can also be passed to the handler. The drawback to this flexibility is that it makes it harder to write truly portable programs that raise stack shortage conditions. Requiring these extra members to take default value of all-bits-zero would make it easier to write portable programs, since such programs could simply initialize the structure using memset() prior to calling '__stackjmp_raise()'.] It is unspecified whether this function is a preprocessor macro or an identifier declared with external linkage. If a macro definition is suppressed in order to access an actual function, or a program defines an external identifier with the same name, the behavior is undefined. If the implementation does not support the semantics of this function or if some other error occurs, this function simply returns. [Rationale: Implementations are strongly encouraged to support the semantics of the '__stackjmp_raise()' function. It is recognized, however, that this might impose unacceptable performance constraints or simply prove impossible for some implementations. Therefore, implementations are not required to support these semantics; they are required, however, to provide this function.] 3. Function __stackjmp_restore() extern int __stackjmp_restore(struct __stackjmpbuf *info); The '__stackjmp_restore()' function re-establishes the object pointed to by 'info' as the current object to be used for subsequent stack shortage conditions that may subsequently occur during program execution. The calling function is not associated with the control object, but rather the function that was associated with the object pointed to by 'info' is reinstated as the function to which to return during a subsequent stack shortage condition [note]. The contents of the object are set in an implementation-defined manner. If 'info' is null, a default stack shortage control object is established instead. Note: It is necessary for a function that establishes a stack shortage control structure to disassociate itself from the stack shortage handling mechanism and re-establish another control handler prior to returning. Failure to do so may lead to unpredictable behavior. [Rationale: Calls to '__stackjmp_set()' and '__stackjmp_restore()' accomplish almost the same thing, in that they establish a stack shortage control structure and a function context to which to return in the event of such a condition. However, the call to '__stackjmp_set()' establishes the calling function as the point to which to return, while the call to '__stackjmp_restore()' establishes a previous function (which called '__stackjmp_set()' at some previous time) as the return point.] The function returns zero if successful, or -1 if an error occurs. It is unspecified whether this function is a preprocessor macro or an identifier declared with external linkage. If a macro definition is suppressed in order to access an actual function, or a program defines an external identifier with the same name, the behavior is undefined. If the implementation does not support the semantics of this function, or if the stack cannot be restored to a stable state after the stack shortage condition occurred, or if some other error occurs, this function returns -1. [Rationale: Implementations are strongly encouraged to support the semantics of the '__stackjmp_restore()' function. It is recognized, however, that this might impose unacceptable performance constraints or simply prove impossible for some implementations. Therefore, implementations are not required to support these semantics; they are required, however, to provide this function.] 4. Function __stackjmp_set() extern struct __stackjmpbuf * __stackjmp_set(volatile struct __stackjmpbuf *info); The '__stackjmp_set()' function manages control information required for detecting and recovering from stack shortage conditions. It returns a pointer value that compares equal to the '_STACKJMP_JUMP' or '_STACKJMP_ERROR' constant, or which points to a stack shortage control object, or which is null. [Discussion: It is intended that the returned pointer value, if it points to a valid object, be treated as a pointer to a constant object, i.e., the object to which it points should not be modified. However, problems arise if the function is declared as returning type 'const struct __stackjmpbuf *', namely that the value returned cannot be passed as the second parameter to a call to '__stackjmp_restore()' unless an explicit cast is used.] This function establishes the object pointed to by 'info' as the current object to be used for subsequent stack shortage conditions that may subsequently occur during program execution. The contents of the object are set in an implementation-defined manner. If the pointer 'info' is null, a default stack shortage control object is established instead. The function returns a pointer to the previously established stack shortage control object; if no such object is currently established (i.e., if this is the very first invocation '__stackjmp_set()' during the execution of the program, or if the previous call to '__stackjmp_set()' or '__stackjmp_restore()' was passed a null 'info' parameter), the function returns null. If an error occurs, the function returns a pointer which compares equal to the '_STACKJMP_ERROR' constant. This function returns a pointer value unequal to the '_STACKJMP_JUMP' constant on its first (normal) invocation within a given calling function. Subsequent calls to other functions may result in a stack shortage condition; such a condition causes the flow of control to return to the point immediately following the last call to '__stackjmp_set()', acting as if the function had returned a second time, and returning a pointer value equal to the '_STACKJMP_JUMP' constant after storing information about the last function invocation that caused the stack shortage into the object pointed to by 'info'. If 'info' is not null, the pointer must point to a valid object of the proper type [note]. Upon the second return from '__stackjmp_set()' (because of a stack shortage condition), the contents of the structure pointed to by 'info' are set to appropriate values, reflecting the information obtained by the runtime environment about the stack shortage condition. In particular, the '__sj_func' member of the structure is set to point to the function to which the unsuccessful call was made (suitably cast to type 'void (*)(void)'). If this information cannot be obtained by the implementation, the '__sj_func' member is set to null. Other members of the structure (if they exist) may also be set to implementation-defined values. Note: It is convenient, though not required, that an object of structure type '__stackjmpbuf' be allocated on the stack as an 'auto' variable local to the calling function. A successful (second) return from the '__stackjmp_set()' function (i.e., a return of a positive value) implies that it is safe for the program to continue execution, i.e., that all necessary cleanup actions have been performed and that the stack has been restored to stable state, resembling the state it was in at the point of the first return from the call to '__stackjmp_set()'. The values of all local (automatic) non-volatile variables in the current execution block are unspecified at the time of the second return from '__stackjmp_set()'. [Rationale: The state of auto variables should be similar to the state of such variables after a non-zero return from setjmp(). This clause should probably be rewritten to reflect this fact.] [Discussion: However, the local control structure within the returned-to function should be in a stable/reliable state after the second return. The above clause may be worded too loosely to ensure this. It is probably wise to mention that the local control structure, as well as any other important local variables in the calling function, should be declared 'volatile'.] It is unspecified whether this function is a preprocessor macro or an identifier declared with external linkage. If a macro definition is suppressed in order to access an actual function, or a program defines an external identifier with the same name, the behavior is undefined. If the implementation does not support the semantics of this function, or if the stack cannot be restored to a stable state after the stack shortage condition occurred, or if some other error occurs, this function returns a pointer value equal to '_STACKJMP_ERROR'. [Rationale: Implementations are strongly encouraged to support the semantics of the '__stackjmp_set()' function. It is recognized, however, that this might impose unacceptable performance constraints or simply prove impossible for some implementations. Therefore, implementations are not required to support these semantics; they are required, however, to provide this function.] Undefined behavior results if the function that made the last call to '__stackjmp_set()' with a non-null 'info' parameter returns without calling '__stackjmp_restore()' and a subsequent stack shortage condition occurs [note]. Note: This implies that any function that establishes a stack shortage control object must restore the previous handler by calling '__stackjmp_restore()' before returning. If this is not done, unpredictable results may occur. EXAMPLE 1 Consider the following program fragment: #include #include static void bar(void) { printf("Entered bar()\n"); // Force a stack shortage condition to occur printf("Forcing a stack shortage\n"); __stackjmp_raise(NULL); // We should not get here printf("Stack did not overflow\n"); } void foo(void) { volatile struct __stackjmpbuf info; struct __stackjmpbuf *volatile prev; struct __stackjmpbuf *volatile old = NULL; // Establish an SOS handler printf("Establish handler\n"); prev = __stackjmp_set(&info); if (prev == _STACKJMP_ERROR) { // __stackjmp_set() semantics are not supported, or error printf("__stackjmp_set() error\n"); // Just terminate exit(EXIT_FAILURE); } else if (prev == _STACKJMP_JUMP) { // Second return from __stackjmp_set(), // Stack shortage detected at the call to bar() printf("Stack shortage caught\n"); } else { // Normal, first return from __stackjmp_set() old = prev; // Cause a stack shortage condition printf("Calling bar()\n"); bar(); // Stack shortage triggered here printf("Returned from bar\n"); } // Restore the previous stack shortage handler printf("Restore handler\n"); __stackjmp_restore(old); printf("Return\n"); } When function 'foo()' is called, the following output should be produced (assuming that the implementation supports the '__stackjmp_set()' function semantics and properly detects a stack shortage condition): Establish handler Calling bar() Entered bar() Forcing a stack shortage Stack shortage caught Restore handler Return EXAMPLE 2 The following program determines whether the implementation supports the '__stackjmp_set()' function semantics or not. #include #include int main(void) { struct __stackjmpbuf *volatile p; #if _STACKJMP_VERSION > 0 // Attempt to retrieve the default stack shortage handler p = __stackjmp_get(); if (p == _STACKJMP_ERROR) printf("__stackjmp_set() is not supported\n"); else printf("__stackjmp_set() is supported\n"); #else printf("__stackjmp_set() is not supported\n"); #endif return (EXIT_SUCCESS); } ACKNOWLEDGEMENTS I wish to thank all those who posted responses and suggestions to the discussion on comp.std.c (circa Jan-Feb 1999), most especially: Clive D. W. Feather Fergus Henderson Nick Maclaren Peter Shenkin ------------------------------------------------------------------------ @(#)text/c0xstack.txt $Revision: 1.11 $ $Date: 2006/06/30 02:57:07 $ ========================================================================