IssueResource 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.
RiskWhen 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);
}
The
allocated file pointer is intended to be deallocated before the function finishes
execution. When an exception takes place, the function exits without deleting the pointer,
which results in a resource leak.
FixTo 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;
}
The
unique pointer
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.
Example — Resource Leak Caused by Exception#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.
Correction — Deallocate Resources Before 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
}
//...
}
}