The C Language facilities setjmp()
and longjmp()
can be used to simulate the throwing and catching of exceptions, but they are very low-level facilities, and using them can bypass proper resource management and the proper calling of destructors.
The C++ Standard ISO/IEC 14882-2003, section 18.7 paragraph 4. says:
The function signature longjmp(jmp_buf jbuf, int val) has more restricted behavior in this International Standard. A setjmp/longjmp call pair has undefined behavior if replacing the setjmp and longjmp by catch and throw would destroy any automatic objects.
Non-Compliant Code Example
Calling longjmp()
prevents local class variables from being properly destroyed, as can be demonstrated by the following code:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <csetjmp> #include <iostream> using namespace std; static jmp_buf env; class Counter { public: static int Instances; Counter() {Instances++;} ~Counter() {Instances--;} private: Counter(const Counter& that); Counter& operator=(const Counter& that); }; int Counter::Instances = 0; class Error {}; void func() { Counter c; cout << "func(): Instances: " << Counter::Instances << endl; longjmp( env, 1); } int main() { cout << "Before setjmp(): Instances: " << Counter::Instances << endl; if (setjmp(env) == 0) { func(); } else { cout << "From longjmp(): Instances: " << Counter::Instances << endl; } cout << "After longjmp(): Instances: " << Counter::Instances << endl; } |
produces, on a Linux machine running g++ 4.3:
Code Block |
---|
Before setjmp(): Instances: 0 func(): Instances: 1 From longjmp(): Instances: 1 After longjmp(): Instances: 1 |
Since the C++ standard leaves this behavior undefined, the compiler merely fails to invoke the destructor for the Counter object.
Compliant Solution
Use exceptions instead of setjmp()
and longjmp()
, as throwing exceptions will still invoke destructors of local class variables.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream> using namespace std; class Counter { public: static int Instances; Counter() {Instances++;} ~Counter() {Instances--;} private: Counter(const Counter& that); Counter& operator=(const Counter& that); }; int Counter::Instances = 0; class Error {}; void func() { Counter c; cout << "func(): Instances: " << Counter::Instances << endl; throw Error(); } int main() { cout << "Before try: Instances: " << Counter::Instances << endl; try { func(); } catch (...) { cout << "In catch: Instances: " << Counter::Instances << endl; } cout << "After catch: Instances: " << Counter::Instances << endl; } |
On the same platform (Linux, g++ 4.3), this code produces:
Code Block |
---|
Before try: Instances: 0 func(): Instances: 1 In catch: Instances: 0 After catch: Instances: 0 |
Exceptions
ERR34-EX1: The longjmp()
function may be safely invoked if you can guarantee that no nontrivial destructors are bypassed between the longjmp()
call and the corresponding setjmp()
.
Risk Assessment
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 |
---|---|---|---|---|---|
ERR34-CPP | low | probable | medium | P4 | L3 |
Bibliography
[ISO/IEC 14882-2003] Section 18.7
[Henricson 97] Rule 13.3 Do not use setjmp()
and longjmp()
.
ERR33-CPP. Destructors must not throw exceptions 12. Exceptions and Error Handling (ERR) ERR35-CPP. A handler in a constructor or destructor's function-try-block should not reference class data