...
Signal masks, floating-point status flags, and the state of open files are not saved by the setjmp()
function. If signal masks need to be saved, the POXIS POSIX sigsetjmp()
function should be used.
This recommendation is related to VOID SIG32SIG30-C. Do not call longjmp() from inside a signal handler and Call only asynchronous-safe functions within signal handlers and ENV32-C. All exit handlers must return normally.
...
Code Block | ||||
---|---|---|---|---|
| ||||
jmp_buf buf; unsigned char b[] = {0xe5, 0x06, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00}; int main(void) { setup(); do_stuff(); return 0; } void setup(void) { f(); } void f(void) { g(); } void g(void#include <setjmp.h> #include <stdio.h> #include <stdlib.h> static jmp_buf buf; static void bad(void); static void g(void) { if (setjmp(buf) == 0) { printf("setjmp() invoked\n"); } else { printf("longjmp() invoked\n"); } } static void do_stufff(void) { char a[8]; memcpy(a, b, 8)g(); } static void setup(void) { f(); } void do_stuff(void) { void (*b)(void) = bad; /* ... */ longjmp(buf, 1); } static void bad(void) { printf("Should not be called!\n"); exit(1); } int main(void) { setup(); do_stuff(); } |
Implementation Details
Compiled for x86at -64 O0 using GCC 4.1.2 on Linux, the preceding 7.5 or Clang 8.0 on Ubuntu 18.04 (Linux for x86-64), the preceding example outputs the following when run:
...
Because g()
has finished executing at the time longjmp()
is called, it is no longer on the stack. When do_stuff()
is invoked, its stack frame occupies the same memory as the old stack frame of g()
. In this case, a
was located in the same location as the return address of function g()
. The call to memcpy()
assignment of b
overwrites the return address, so when longjmp()
sends control back to function g()
, the function returns to the wrong address (in this case, to function bad()
).
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <setjmp.h> #include <stdio.h> #include <stdlib.h> static jmp_buf buf; unsignedstatic char b[] = {0xe5, 0x06, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00}void bad(void); intvoid maindo_stuff(void) { ifvoid (setjmp*b)(bufvoid) == 0) { printf("setjmp() invoked\n"); } else { bad; /* ... */ longjmp(buf, 1); } static void bad(void) { printf("longjmp() invokedShould not be called!\n"); } do_stuff(exit(1); return 0; } voidint do_stuffmain(void) { char a[8]; memcpy(a, b, 8); /* ... */ longjmp(buf, 1); } void bad(void)if (setjmp(buf) == 0) { printf("setjmp() invoked\n"); } else { printf("Should not be called!longjmp() invoked\n"); } exitdo_stuff(1); } |
There is no risk of overwriting a return address because the stack frame of main()
(the function that invoked setjmp()
) is still on the stack; so when do_stuff()
is invoked, the two stack frames will not overlap.
...
Code Block | ||||
---|---|---|---|---|
| ||||
jmp_buf buf; void f(void) { int i = 0; if (setjmp(buf) != 0) { printf("%i\n", i); /* ... */ } i = 2; g(); } void g(void) { /* ... */ longjmp(buf, 1); } |
Implementation Details
Calling f()
will print 2
if you compile with -O0
, but will print 0
if you compile with -O2
. This involves using GCC 7.5 or Clang 8.0 on Ubuntu 18.04 (Linux x86-64).
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:
Code Block | ||||
---|---|---|---|---|
| ||||
jmp_buf buf; void f(void) { volatile int i = 0; if (setjmp(buf) != 0) { printf("%i\n", i); /* ... */ } i = 2; g(); } void g(void) { /* ... */ longjmp(buf, 1); } |
This will now correctly print 2
regardless of optimization level.
Risk Assessment
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
MSC22-C | Low | Probable | Medium | P4 | L3 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
CodeSonar |
| BADFUNC.LONGJMP BADFUNC.SETJMP | Use of longjmp Use of setjmp |
...
LDRA tool suite |
| 43 S | Enhanced enforcement | ||||||
Parasoft C/C++test |
| CERT_C-MSC22-a | The facilities provided by <setjmp.h> should not be used | ||||||
Polyspace Bug Finder |
| CERT C: Rec. MSC22-C | Checks for use of setjmp/longjmp (rec. fully covered) | ||||||
SonarQube C/C++ Plugin |
| S982 |
...