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

Compare with Current View Page History

« Previous Version 28 Next »

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:

#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:

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.

#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:

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

  • No labels