The C standard library facilities setjmp() and longjmp() can be used to simulate throwing and catching exceptions. However, these facilities bypass automatic resource management and can result in undefined behavior, commonly including resource leaks, and denial-of-service attacks.
The C++ Standard, [support.runtime], paragraph 4 [ISO/IEC 14882-2014], states:
The function signature
longjmp(jmp_buf jbuf, int val)has more restricted behavior in this International Standard. Asetjmp/longjmpcall pair has undefined behavior if replacing thesetjmpandlongjmpbycatchandthrowwould invoke any non-trivial destructors for any automatic objects.
Do not call setjmp() or longjmp(); their usage can be replaced by more standard idioms such as throw expressions and catch statements.
Calling longjmp() such that it would invoke a nontrivial destructor were the call replaced with a throw expression results in undefined behavior, as demonstrated in this noncompliant code example:
#include <csetjmp>
#include <iostream>
static jmp_buf env;
struct Counter {
static int Instances;
Counter() { ++Instances; }
~Counter() { --Instances; }
};
int Counter::Instances = 0;
void f() {
Counter c;
std::cout << "f(): Instances: " << Counter::Instances << std::endl;
std::longjmp(env, 1);
}
int main() {
std::cout << "Before setjmp(): Instances: " << Counter::Instances << std::endl;
if (setjmp(env) == 0) {
f();
} else {
std::cout << "From longjmp(): Instances: " << Counter::Instances << std::endl;
}
std::cout << "After longjmp(): Instances: " << Counter::Instances << std::endl;
}
|
The above code produces the following results when compiled with Clang 3.5 for Linux, demonstrating that the undefined behavior in this instance is to fail to destroy the local Counter instance when the execution of f() is terminated:
Before setjmp(): Instances: 0 f(): Instances: 1 From longjmp(): Instances: 1 After longjmp(): Instances: 1 |
This compliant solution replaces the calls to setjmp() and longjmp() with a throw expression and a catch statement:
#include <iostream>
struct Counter {
static int Instances;
Counter() { ++Instances; }
~Counter() { --Instances; }
};
int Counter::Instances = 0;
void f() {
Counter c;
std::cout << "f(): Instances: " << Counter::Instances << std::endl;
throw "Exception";
}
int main() {
std::cout << "Before throw: Instances: " << Counter::Instances << std::endl;
try {
f();
} catch (const char *E) {
std::cout << "From catch: Instances: " << Counter::Instances << std::endl;
}
std::cout << "After catch: Instances: " << Counter::Instances << std::endl;
}
|
which produces the following output:
Before throw: Instances: 0 f(): Instances: 1 From catch: Instances: 0 After catch: Instances: 0 |
Using setjmp() and longjmp() could lead to a denial-of-service attack due to resources not being properly destroyed.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
ERR52-CPP | Low | Probable | Medium | P4 | L3 |
Tool | Version | Checker | Description |
|---|---|---|---|
| Clang | cert-err52-cpp | Checked by clang-tidy. | |
| Parasoft C/C++test | 9.5 | MISRA2012-RULE-21_4_{a,b}, JSF-020 | |
Secondary analysis |
Search for other vulnerabilities resulting from the violation of this rule on the CERT website.
| [Henricson 97] | Rule 13.3, Do not use setjmp() and longjmp() |
| [ISO/IEC 14882-2014] | Subclause 18.10, "Other Runtime Support" |