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

Compare with Current View Page History

« Previous Version 11 Next »

Thrown exceptions that are not explicitly caught subject a program to several implementation-dependent issues. C++2004, section 15.3 "Handling an Exception", says:

If no matching handler is found in a program, the function std::terminate() is called; whether or not the stack is unwound before this call to std::terminate() is implementation-defined (15.5.1).

Consequently you should take steps to prevent std::terminate() from being invoked for two reasons. First because it involves implementation-defined behavior. Second, if the stack is not unwound on your platform, than RAII is violated. That is, destructors are not called, allocated memory is not freed, opened files are not flushed and closed, etc.

Non-Compliant Code Example (main())

In this example, main() does several useful work but does not catch any exceptions. Consequently, any exceptions thrown will call std::terminate(), and might not destroy any objects owned by the program.

int main(int argc, char** argv) {
  Object object; // might not get destroyed if exception thrown
  // do useful work
  return 0;
}

Compliant Solution (main())

In this code example, all exceptions are caught, allowing normal termination, even in the face of unexpected errors (albeit with an exit status indicating that an error occurred).

int main(int argc, char** argv) {
  Object object;
  int exit_status = EXIT_SUCCESS;

  try {
    // do useful work
  } catch (...) {
    exit_status = EXIT_FAILURE;
  }

  return exit_status; // object gets destroyed here
}

Compliant Solution (main())

An alternative is to wrap all of main()'s functionality inside a try-catch block and catch and handle exceptions by exiting with a status indicating an error to the invoking process.

int main(int argc, char** argv) {
  try {
    Object object;
    // do useful work
    return 0; // object gets destroyed here
  } catch (...) {
    exit(EXIT_FAILURE);  
  }
}

Non-Compliant Code Example (throw() Declaration)

A function that declares exception specifications must list all unrelated exception classes that might be thrown during its invocation. If an exception is thrown that is not related to any of those listed in its exception specification, control automatically reverts to std::unexpected(), which does not return.

In the following code example, the function f() claims to throw exception1 but actually throws exception2. Consequently control flow is diverted to std::unexpected, and the toplevel catch clause may not be invoked. (It is not invoked on Linux with G++ 4.3).

using namespace std;
class exception1 : public exception {};
class exception2 : public exception {};

void f(void) throw( exception1) {
  // ...
  throw (exception2());
}

int main() {
  try {
    f();
    return 0;
  } catch (...) {
    cerr << "F called" << endl;
  }
  return EXIT_FAILURE;
}

Compliant Solution (throw() Declaration)

The following code example declares the same exception it actually throws

using namespace std;
class exception1 : public exception {};
class exception2 : public exception {};

void f(void) throw( exception1) {
  // ...
  throw (exception1());
}

int main() {
  try {
    f();
    return 0;
  } catch (...) {
    cerr << "F called" << endl;
  }
  return EXIT_FAILURE;
}

Risk Assessment

Failing to handle exceptions can lead to resources not being freed, closed, etc.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

ERR12-C

1 (low)

1 (unlikely)

1 (low)

P1

L3

Other Languages

This rule appears in the Java Secure Coding Standard as EXC08-J. Try to gracefully recover from system errors.

References

[[ISO/IEC 14882-2003]]
[[MISRA 08]] Rule 15-3-2, 15-3-4


ERR09-CPP. Throw anonymous temporaries and catch by reference      12. Exceptions and Error Handling (ERR)      ERR31-CPP. Don't redefine errno

  • No labels