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
}
//...
}
}