You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

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"

  • No labels