The C Language facilities setjmp()
and longjmp()
can be used as a poor attempt 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.
...
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 |
---|---|---|---|---|---|
RES39-C | 1 (low) | 2 (probable) | 2 (medium) | P4 | L3 |
...