Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Wiki MarkupNot all exceptions can be caught. Quoting from \caught, even with careful use of function-try-blocks. The C++ Standard, [except.handle\], p13 of [N3000|http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n3000.pdf], the current Working Draft of the C+\+ standardparagraph 13 [ISO/IEC 14882-2014], states the following:

Exceptions thrown in destructors of objects with static storage duration or in constructors of namespace - scope objects with static storage duration are not caught by a function-try-block on main() . Exceptions thrown Exceptions thrown in destructors of objects with thread storage duration or in constructors of namespace-scope objects with objects with thread storage duration are not caught by a function-try-block on the initial function of the thread.

To illustrate using an example:

When declaring an object with static or thread storage duration, and that object is not declared within a function block scope, the type's constructor must be declared noexcept and must comply with ERR55-CPP. Honor exception specifications. Additionally, the initializer for such a declaration, if any, must not throw an uncaught exception (including from any implicitly constructed objects that are created as a part of the initialization). If an uncaught exception is thrown before main() is executed, or if an uncaught exception is thrown after main() has finished executing, there are no further opportunities to handle the exception and it results in implementation-defined behavior. (See ERR50-CPP. Do not abruptly terminate the program for further details.)

For more information on exception specifications of destructors, see DCL57-CPP. Do not let exceptions escape from destructors or deallocation functions.

Noncompliant Code Example

In this noncompliant example, the constructor for S may throw an exception that is not caught when globalS is constructed during program startup.

Code Block
bgColor#FFcccc
langcpp
struct S {
  S() noexcept(false);
};
 
static S globalS;

Compliant Solution

This compliant solution makes globalS into a local variable with static storage duration, allowing any exceptions thrown during object construction to be caught because the constructor for S will be executed the first time the function globalS() is called rather than at program startup. This solution does require the programmer to modify source code so that previous uses of globalS are replaced by a function call to globalS().

Code Block
bgColor#ccccff
langcpp
struct S {
  S() noexcept(false);
};
 
S &globalS() {
  try {
    static S s;
    return s;
  } catch 
Code Block

struct Foo {
  Foo();    // may throw
};

Foo A;

void bar() {
  static Foo B;
}

int main()
try {
  bar();
  // other executable statements
}
catch(...) {
    // willHandle catcherror, exceptionsperhaps thrownby fromlogging bar()it and gracefully terminating the application.
  }
  // any other executable statements in the tryUnreachable.
}

Noncompliant Code Example

In this noncompliant example, the constructor of global may throw an exception during program startup. (The std::string constructor, which accepts a const char * and a default allocator object, is not marked noexcept and consequently allows all exceptions.) This exception is not caught by the function-try-block on main(), resulting in a call to std::terminate() and abnormal program termination.

Code Block
bgColor#FFcccc
langcpp
#include <string>
 
static const std::string global("...");

int main()
try {
  // block above

 ...
} catch(...) {
  // IMPORTANT: willWill not catch exceptions thrown
  // from the constructor of the  global
}

Compliant Solution

Compliant code must prevent exceptions from escaping during program startup and termination. This compliant solution avoids defining a std::string at global namespace scope and instead uses a static const char *.

Code Block
bgColor#ccccff
langcpp
static const char *global = "...";

int main() {global object A
  // ...
}

Compliant Solution

This compliant solution introduces a class derived from std::string with a constructor that catches all exceptions with a function try block and terminates the application in accordance with ERR50-CPP-EX1 in ERR50-CPP. Do not abruptly terminate the program in the event any exceptions are thrown. Because no exceptions can escape the constructor, it is marked noexcept and the class type is permissible to use in the declaration or

...

initialization of a static global variable.

For brevity, the full interface for such a type is not described.

Code Block
bgColor#ccccff
langcpp
#include <exception>
#include <string>

namespace my {
struct string : std::string {
  explicit string(const char *msg,
                  const std::string::allocator_type &alloc = std::string::allocator_type{}) noexcept
  try : std::string(msg, alloc) {} catch(...) {
    extern void log_message(const char *) noexcept;
    log_message("std::string constructor threw an exception");
    std::terminate();
  }
  // ...
};
}
 
static const my::string global("...");

int main() {
  // ...
}

Noncompliant Code Example

In this noncompliant example, an exception may be thrown by the initializer for the static global variable i.

Code Block
bgColor#FFcccc
langcpp
extern int f() noexcept(false);
int i = f();
 
int main() {
  // ...
}

Compliant Solution

This compliant solution wraps the call to f() with a helper function that catches all exceptions and terminates the program in conformance with ERR50-CPP-EX1 of ERR50-CPP. Do not abruptly terminate the program.

Code Block
bgColor#ccccff
langcpp
#include <exception>
 
int f_helper() noexcept {
  try {
    extern int f() noexcept(false);
    return f();
  } catch (...) {
    extern void log_message(const char *) noexcept;
    log_message("f() threw an exception");
    std::terminate();
  }
  // Unreachable.
}
 
int i = f_helper();

int main() {
  // ...
}

Risk Assessment

Throwing an exception that cannot be caught results in abnormal program termination and can lead to denial-of-service attacks.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

ERR58-CPP

Low

Likely

Low

P9

L2

Automated Detection

Tool

Version

Checker

Description

Astrée

Include Page
Astrée_V
Astrée_V

potentially-throwing-static-initialization
Partially checked
Axivion Bauhaus Suite

Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V

CertC++-ERR58
Clang
Include Page
Clang_38_V
Clang_38_V
cert-err58-cppChecked by clang-tidy
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

LANG.STRUCT.EXCP.THROW

Use of throw

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C++4634, C++4636, C++4637, C++4639


Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V
CERT_CPP-ERR58-a

Exceptions shall be raised only after start-up and before termination of the program

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C++: ERR58-CPPChecks for exceptions raised during program startup (rule fully covered)
RuleChecker
Include Page
RuleChecker_V
RuleChecker_V
potentially-throwing-static-initialization
Partially checked

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

This rule is a subset of ERR50-CPP. Do not abruptly terminate the program

Bibliography

[ISO/IEC 14882-2014]Subclause 15.4, "Exception Specifications"
[Sutter 2000]Item 8, "Writing Exception-Safe Code—Part 1"


...

Image Added Image Added Image AddedThus, it is important to prevent constructors of objects with static storage duration to throw exceptions. See also ERR33-CPP. Destructors must not throw exceptions.