Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Parasoft C/C++test 2022.2

The setjmp() macro should be invoked from only one of the contexts listed in Section subclause 7.13.1.1 of the C Standard [ISO/IEC 9899:2011]. Invoking setjmp() outside of one of these contexts results in undefined behavior. (See undefined behavior 125.)

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. (See undefined behaviors 127 and 10.)

...

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 the POSIX sigsetjmp() POSIX function should be used.

This recommendation is related to 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.

...

This noncompliant code example calls setjmp() in an assignment statement, resulting in undefined behavior:

Code Block
bgColor#FFCCCC
langc
jmp_buf buf;

void f(void) {
  int i = setjmp(buf);
  if (i == 0) {
    g();
  } else {
    /* longjmp was invoked */
  }
}

void g(void) {
  /* ... */
  longjmp(buf, 1);
}

...

Any attempt to invoke the longjmp() function to transfer control to a function that has completed execution results in undefined behavior:

Code Block
bgColor#FFCCCC
langc
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);
  /* ... stuffg();
}

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 version 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()).

If the array b were user - specified, the user would be able to set the return address of function g() to any location.

...

Code Block
bgColor#ccccff
langc
#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();
  return 0exit(1);
}

voidint do_stuffmain(void) {
  char a[8];
  memcpy(a, b, 8);
  /* ... stuff ... */
  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.

...

In this noncompliant example, non-volatile-qualified objects local to the function that invoked the corresponding setjmp() have indeterminate values after longjmp() is executed if their value has been changed since the invocation of setjmp():

Code Block
bgColor#FFCCCC
langc
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
bgColor#ccccff
langc
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

Low

probable

Probable

medium

Medium

P4

L3

...

Automated Detection

ToolVersionCheckerDescription
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

BADFUNC.LONGJMP

BADFUNC.SETJMP

Use of longjmp

Use of setjmp

LDRA tool suite
Include Page
LDRA_V
LDRA_V
43 SEnhanced enforcement
Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V

CERT_C-MSC22-a

The facilities provided by <setjmp.h> should not be used
Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rec. MSC22-CChecks for use of setjmp/longjmp (rec. fully covered)


SonarQube C/C++ Plugin
Include Page
SonarQube C/C++ Plugin_V
SonarQube C/C++ Plugin_V
S982


...

Image Modified Image Modified Image Modified