The C Language standard library 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. However, these facilities bypass automatic resource management and can result in undefined behavior, commonly including resource leaks and denial-of-service attacks.
The C++ Standard, [support.runtime], paragraph 4 [ISO/IEC 14882-2003, section 18.7 paragraph 4. says2014], states the following:
The function signature
longjmp(jmp_buf jbuf, int val)
has has more restricted behavior in this International StandardInternational Standard. Asetjmp
/longjmp
call call pair has undefined behavior if replacing thesetjmp
and andlongjmp
by bycatch
and throw would destroy any automatic objects.
Non-Compliant Code Example
and
throw
would invoke any non-trivial destructors for any automatic objects.
Do not call setjmp()
or longjmp()
; their usage can be replaced by more standard idioms such as throw
expressions and catch
statements.
Noncompliant Code Example
If a throw
expression would cause a nontrivial destructor to be invoked, then calling longjmp()
in the same context will result in undefined behavior. In the following noncompliant code example, the call to longjmp()
occurs in a context with a local Counter
object. Since this object’s destructor is nontrivial, undefined behavior results.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; classstruct Counter { public: static int Instancesinstances; Counter() {Instances ++instances; } ~Counter() {Instances --instances;} private: Counter(const Counter& that); Counter& operator=(const Counter& that); } }; int Counter::Instancesinstances = 0; class Error {}; void funcf() { Counter c; std::cout << "funcf(): Instances: " << Counter::Instancesinstances << std::endl; std::longjmp( env, 1); } int main() { std::cout << "Before setjmp(): Instances: " << Counter::Instancesinstances << std::endl; if (setjmp(env) == 0) { funcf(); } else { std::cout << "From longjmp(): Instances: " << Counter::Instancesinstances << std::endl; } std::cout << "After longjmp(): Instances: " << Counter::Instancesinstances << std::endl; } |
Implementation Details
The above code produces the following results when compiled with Clang 3.8 for Linux, on a Linux machine running g++ 4.3:demonstrating that the program, on this platform, fails to destroy the local Counter
instance when the execution of f()
is terminated. This is permissible as the behavior is undefined.
Code Block |
---|
Code Block |
Before setjmp(): Instances: 0 funcf(): Instances: 1 From longjmp(): Instances: 1 After longjmp(): Instances: 1 |
...
Compliant Solution
Use exceptions instead of This compliant solution replaces the calls to setjmp()
and and longjmp()
, as throwing exceptions will still invoke destructors of local class variables with a throw
expression and a catch
statement.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream> using namespace std; class struct Counter { public: static int Instancesinstances; Counter() {Instances ++instances; } ~Counter() {Instances --instances; } private: Counter(const Counter& that); Counter& operator=(const Counter& that); }; int Counter::Instancesinstances = 0; class Error {}; void funcf() { Counter c; std::cout << "funcf(): Instances: " << Counter::Instancesinstances << std::endl; throw Error()"Exception"; } int main() { std::cout << "Before trythrow: Instances: " << Counter::Instancesinstances << std::endl; try { funcf(); } catch (...const char *E) { std::cout << "InFrom catch: Instances: " << Counter::Instancesinstances << std::endl; } std::cout << "After catch: Instances: " << Counter::Instancesinstances << std::endl; } |
On the same platform (Linux, g++ 4.3), this code produces:This solution produces the following output.
Code Block |
---|
Before trythrow: Instances: 0 funcf(): Instances: 1 InFrom catch: Instances: 0 After catch: Instances: 0 |
...
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 |
---|
ERR52-CPP |
1 (low)
2 (probable)
Low | Probable | Medium | P4 | L3 |
References
Wiki Markup |
---|
\[[ISO/IEC 14882-2003|AA. C++ References#ISO/IEC 14882-2003]\] Section 18.7
\[[Henricson 97|AA. C++ References#Henricson 97]\] Rule 13.3 Do not use {{setjmp()}} and {{longjmp()}}. |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Astrée |
| include-setjmp | Fully checked | ||||||
Axivion Bauhaus Suite |
| CertC++-ERR52 | |||||||
Clang |
| cert-err52-cpp | Checked by clang-tidy . | ||||||
CodeSonar |
| BADFUNC.LONGJMP | Use of longjmp Use of setjmp | ||||||
Helix QAC |
| C++5015 | |||||||
Klocwork |
| MISRA.STDLIB.LONGJMP | |||||||
LDRA tool suite |
| 43 S | Fully implemented | ||||||
Parasoft C/C++test |
| CERT_CPP-ERR52-a | The facilities provided by <setjmp.h> should not be used | ||||||
Polyspace Bug Finder |
| CERT C++: ERR52-CPP | Checks for use of setjmp/longjmp (rule fully covered) | ||||||
RuleChecker |
| include-setjmp | Fully checked | ||||||
SonarQube C/C++ Plugin |
| S982 |
Related Vulnerabilities
Search for other vulnerabilities resulting from the violation of this rule on the CERT website.
Bibliography
[Henricson 1997] | Rule 13.3, Do not use setjmp() and longjmp() |
[ISO/IEC 14882-2014] | Subclause 18.10, "Other Runtime Support" |
...
ERR33-CPP. Destructors must be exception-safe 12. Exceptions and Error Handling (ERR) 13. Object Orientation (OBJ)