The C Standard permits setjmp
to be implemented as either a macro or a function. If it is implemented as a function, it will not know enough about the calling environment to save temporary registers or dynamic stack locations. This limits the possible complexity of expressions containing setjmp
. For this reason, setjmp
should only be invoked from one of the following contexts:
- the entire controlling expression of a selection or iteration statement
- one operand of a relational or equality operator with the other operand an integer constant expression, with the resulting expression being the entire controlling expression of a selection or iteration statement
- the operand of a unary
!
operator with the resulting expression being the entire controlling expression of a selection or iteration statement - the entire expression of an expression statement (possibly cast to void)
After invoking longjmp
, non-volatile-qualified local objects should not be accessed if their values could have changed since the invocation of setjmp
. Their value in this case is considered indeterminate and accessing them is undefined behavior.
longjmp
should never be used to return control to a function that has terminated execution.
This recommendation is related to SIG32-C. Do not call longjmp() from inside a signal handler and ENV32-C. All atexit handlers must return normally.
Noncompliant Code Example
The following noncompliant code example calls setjmp
in an assignment statement, resulting in undefined behavior.
jmp_buf buf; void f() { int i = setjmp(buf); if (i == 0) { g(); } else { /* longjmp was invoked */ } } void g() { /* ... */ longjmp(buf, 1); }
Compliant Solution
Placing the call to setjmp
in the if
statement and (optionally) comparing it with a constant integer removes the undefined behavior.
jmp_buf buf; void f() { if (setjmp(buf) == 0) { g(); } else { /* longjmp was invoked */ } } void g() { /* ... */ longjmp(buf, 1); }
Noncompliant Code Example
Any attempt to longjmp
to a function that has terminated execution results in possibly exploitable undefined behavior.
jmp_buf buf; void f() { g(); h(); return; } void g() { if (setjmp(buf) != 0) { /* longjmp was invoked*/ } return; } void h() { /* ... */ longjmp(buf, 1); }
Compliant Solution
longjmp
should only be used when the function containing the corresponding setjmp
is guaranteed not to have terminated execution, as in the following example.
jmp_buf buf; void f() { if (setjmp(buf) != 0) { /* longjmp was invoked */ } h(); return; } void h() { /* ... */ longjmp(buf, 1); }
Noncompliant Code Example
Non-volatile-qualified objects local to the function that invoked the corresponding setjmp
have indeterminate values after longjmp
has been executed if their value has been changed since the invocation of setjmp
.
jmp_buf buf; void f() { int i = 0; if (setjmp(buf) != 0) { printf("%i\n", i); /* ... */ } i = 2; g(); } void g() { /* ... */ longjmp(buf, 1); }
Compliant Solution
If an object local to the function that invoked setjmp
needs to be accessed after longjmp
returns control to the function, the object should be volatile-qualified.
jmp_buf buf; void f() { volatile int i = 0; if (setjmp(buf) != 0) { printf("%i\n", i); /* ... */ } i = 2; g(); } void g() { /* ... */ longjmp(buf, 1); }
Risk Assessment
Recommendation |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
MSC22-C |
low |
probably |
low |
P6 |
L2 |
References
[[ISO/IEC 9899:1999]] Section 7.13, "Nonlocal jumps <setjmp.h>"
[[ISO/IEC 9899:1999]] Section J.2, "Portability issues"