CERT C++: ERR57-CPP
Description
Rule Definition
Do not leak resources when handling exceptions.1
Polyspace Implementation
The rule checker checks for these issues:
Resource leak caused by exception
Object left in partially initialized state
Bad allocation in constructor
Examples
Resource leak caused by exception
Resource leak caused by exception occurs when a function
raises an unhandled exception by using a throw
statement but does not
deallocate the resources that were allocated before the exception.
When a function raises an unhandled exception it immediately goes out of scope. If the function manages resources and they are not deallocated prior to raising the exception, the resource is leaked. Consider this code:
FILE* FilePtr; //... void foo(){ FilePtr = fopen("some_file.txt", "r"); //... if(/*error condition*/) throw ERROR_CODE; //... fclose(FilePtr); }
To fix this defect, a function must set all resources that it allocates to a valid state
before it goes out of scope. In the preceding code example, the function must delete the
pointer FilePtr
before the throw
statement.
Instead of manually tracking the allocation and deallocation of resources, the best practice is to follow either the Resource Acquisition Is Initialization (RAII) or the Constructor Acquires, Destructor Releases (CADre) design patterns. Resource allocation is performed in constructors and resource deallocation is performed in destructors. The lifecycle of resources are controlled by scope-bound objects. When functions reach the end of their scope, the acquired resources are properly released. Consider this code:
void releaseFile(std::FILE* fp) { std::fclose(fp); } std::unique_ptr<std::FILE, decltype(&releaseFile)> FilePtr; //... void foo(){ FilePtr(std::fopen("some_file.txt"),&releaseFile); //... if(/*error condition*/) throw ERROR_CODE; }
FilePTR
invokes the function
releaseFile
to delete the allocated resource once the function
foo
reaches the end of its scope. Whether the function exits normally
with an unhandled exception, the allocated resources are deallocated.C++ smart pointers such as std::unique_ptr
and
std::shared_ptr
follow the RAII pattern. They simplify managing the
lifecycle of resources during exception handling. Whenever possible, avoid using raw
pointers.
#include <cstdint> #include <memory> #include <stdexcept> extern int sensorFlag() noexcept; namespace Noncompliant{ void func(){ int* intPtr = new int; int data = sensorFlag(); if(data==-1)//Error throw std::runtime_error("Unexpected value");//Noncompliant //... delete intPtr; } }
In this example, the function Noncompliant::func()
manages the raw
pointer inPtr
. The function allocates memory for it, and then releases
the memory after some operations. The function exits with an exception when
data
is -1
. In this case, the function exits
before releasing the allocated memory, resulting in a memory leak. Polyspace® flags the throw
statement.
throw
StatementsTo prevent memory leak, the allocated memory must be released before raising the
exception, as shown in Compliant::func
.
The best practice is to follow the RAII design pattern. For instance, when C++14 is
available, use unique_ptr
instead of a raw pointer.
BestPractice::func
shows an implementation of func
that follows the RAII pattern. The memory lifecycle is managed by the object itself. That
is, once func
is out of scope, the smart pointer
intPtr
deletes itself and releases the memory. Because the memory
management is performed correctly by the smart pointer,
BestPractice::func
is simpler and safer.
#include <cstdint> #include <memory> #include <stdexcept> extern int sensorFlag() noexcept; namespace Compliant{ void func(){ int* intPtr = new int; int data = sensorFlag(); if(data==-1){//Error delete intPtr; throw std::runtime_error("Unexpected value");//Compliant } //... delete intPtr; } } namespace BestPractice{// C++14 void func(){ std::unique_ptr<int> intPtr = std::make_unique<int>(); int data = sensorFlag(); if(data==-1){//Error throw std::runtime_error("Unexpected value");//Compliant } //... } }
Object left in partially initialized state
Object left in partially initialized state occurs
when a noexcept(false)
constructor raises an unhandled exception but
does not deallocate the resources that were allocated before the exception. This issue is
detected only in classes that your code uses.
A constructor goes out of scope when it raises an unhandled exception. If the constructor manages resources and they are not deallocated prior to raising the exception, the object is left in a partially initialized state. This behavior is undefined and can produce unexpected results.
To fix this defect, keep track of the allocated resources and deallocate them before raising exception.
Instead of manually tracking the allocation and deallocation of resources, the best practice is to follow either the Resource Acquisition Is Initialization (RAII) or the Constructor Acquires, Destructor Releases (CADre) design patterns. Resource allocation is performed in constructors and resource deallocation is performed in destructors. The lifecycle of resources are controlled by scope-bound objects. When functions reach the end of their scope, the acquired resources are properly released. Consider this code:
class complex_ptr{ complex_ptr() = default; ~complex_ptr() = default; private: std::unique_ptr<std::complex<double> > z; };
complex_ptr
uses the implicit default constructor because the
resource management is performed by the smart pointer class unique_ptr
.
The default constructor does not raise exceptions and the object is not left in a
partially initialized state.C++ smart pointers such as std::unique_ptr
and
std::shared_ptr
follow the RAII pattern. They simplify managing the
lifecycle of resources during exception handling. Whenever possible, avoid using raw
pointers.
##include<cstdlib> #include<exception> class complex_ptr{ complex_ptr(){ real = (double*)malloc(sizeof(double)); imag = (double*)malloc(sizeof(double)); if(real==nullptr || imag==nullptr){ throw std::exception(); //Noncompliant } } ~complex_ptr(){ free(real); free(imag); } private: double* real; double* imag; }; void foo(void){ complex_ptr Z; //... }
In this example, the class complex_ptr
is responsible for
allocating and deallocating two raw pointers to double
. The constructor
complex_ptr::complex_ptr()
terminates with an exception when a memory
allocation operation fails. The constructor exits without deallocating the allocated
resources, resulting in a partially constructed object. Polyspace flags the throw
statement in the constructor.
To correct this defect, deallocate the allocated resources before raising exceptions
in constructor. In this code, before raising the exception, the constructor deallocates
the allocated memory by calling deallocate()
. This constructor is
compliant with this rule.
#include<cstdlib> #include<exception> class complex_ptr{ complex_ptr(){ real = (double*)malloc(sizeof(double)); imag = (double*)malloc(sizeof(double)); if(real==nullptr || imag==nullptr){ deallocate(); throw std::exception(); //Compliant } } void deallocate(){ free(real); free(imag); } ~complex_ptr(){ deallocate(); } private: double* real; double* imag; }; void foo(void){ complex_ptr Z; //... }
Bad allocation in constructor
Bad allocation in constructor occurs when a
new
operation is performed in a constructor without using the
argument std::nothrow
or outside exception handling blocks such as
try
or function-try
.
The new
operations might fail and raise a
std::bad_alloc
exception. If these statements are not enclosed in a
try
or function-try
block, the exception might
cause an abrupt termination of a constructor. Such an abrupt termination might leave the
object in a partially constructed state, which is undefined behavior in the C++
standard.
When using the new
operator, enclose it in a try
or function-try
block.
#include<cstdlib> #include <stdexcept> #include <new> class complex_ptr{ complex_ptr(): real(new double), imag(new double){ //Noncompliant } ~complex_ptr(){ delete real; delete imag; } private: double* real; double* imag; }; void foo(void){ complex_ptr Z; //... }
In this example, the constructor of complex_ptr
performs
new
operations that might raise exceptions. Because the constructor
has no mechanism for handling these exceptions, they might cause the constructor to
abruptly terminate. Such termination might leave the object in partially defined state
because the allocated resources are not deallocated. Polyspace flags the constructor.
new
Operations in
ConstructorsTo correct this defect, perform the new operation in a try
or
function-try
block.
#include<cstdlib> #include <stdexcept> #include <new> class complex_ptr{ complex_ptr()try: real(new double), imag(new double){ //Compliant }catch(std::bad_alloc){ //... } ~complex_ptr(){ delete real; delete imag; } private: double* real; double* imag; }; void foo(void){ complex_ptr Z; //... }
Check Information
Group: Rule 08. Exceptions and Error Handling (ERR) |
Version History
Introduced in R2021a
1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.
ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.
MATLAB Command
You clicked a link that corresponds to this MATLAB command:
Run the command by entering it in the MATLAB Command Window. Web browsers do not support MATLAB commands.
Select a Web Site
Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .
You can also select a web site from the following list
How to Get Best Site Performance
Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.
Americas
- América Latina (Español)
- Canada (English)
- United States (English)
Europe
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)
Asia Pacific
- Australia (English)
- India (English)
- New Zealand (English)
- 中国
- 日本Japanese (日本語)
- 한국Korean (한국어)