...
In this compliant solution, the dynamically allocated memory is not deallocated until it is no longer required:.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <new> struct S { void f(); }; void g() noexcept(false) { S *s = new S; // ... s->f(); delete s; } |
...
When possible, use automatic storage duration instead of dynamic storage duration. Since s
is not required to live beyond the scope of g()
, this compliant solution uses automatic storage duration to limit the lifetime of s
to the scope of g()
:.
Code Block | ||||
---|---|---|---|---|
| ||||
struct S { void f(); }; void g() { S s; // ... s.f(); } |
...
In the following noncompliant code example, the dynamically allocated memory managed by the buff
object is accessed after it has been implicitly deallocated by the object's destructor:.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream> #include <memory> #include <cstring> int main(int argc, const char *argv[]) { const char *s = ""; if (argc > 1) { enum { BufferSize = 32 }; try { std::unique_ptr<char[]> buff(new char[BufferSize]); std::memset(buff.get(), 0, BufferSize); // ... s = std::strncpy(buff.get(), argv[1], BufferSize - 1); } catch (std::bad_alloc &) { // Handle error } } std::cout << s << std::endl; } |
...
In this compliant solution, the lifetime of the buff
object extends past the point at which the memory managed by the object is accessed:.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream> #include <memory> #include <cstring> int main(int argc, const char *argv[]) { std::unique_ptr<char[]> buff; const char *s = ""; if (argc > 1) { enum { BufferSize = 32 }; try { buff.reset(new char[BufferSize]); std::memset(buff.get(), 0, BufferSize); // ... s = std::strncpy(buff.get(), argv[1], BufferSize - 1); } catch (std::bad_alloc &) { // Handle error } } std::cout << s << std::endl; } |
...
In this compliant solution, a variable with automatic storage duration of type std::string
is used in place of the std::unique_ptr<char[]>
, which reduces the complexity and improves the security of the solution:.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream> #include <string> int main(int argc, const char *argv[]) { std::string str; if (argc > 1) { str = argv[1]; } std::cout << str << std::endl; } |
...
In this compliant solution, a local copy of the string returned by str_func()
is made to ensure that string str
will be valid when the call to display_string()
is made:.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <string> std::string str_func(); void display_string(const char *s); void f() { std::string str = str_func(); const char *cstr = str.c_str(); display_string(cstr); /* ok */ } |
...
The compliant solution depends on programmer intent. If the programmer intends to allocate a single unsigned char
object, the compliant solution is to use new
instead of a direct call to operator new()
, as this compliant solution demonstrates:.
Code Block | ||||
---|---|---|---|---|
| ||||
void f() noexcept(false) { unsigned char *ptr = new unsigned char; *ptr = 0; // ... delete ptr; } |
...
If the programmer intends to allocate zero bytes of memory (perhaps in order to obtain a unique pointer value that cannot be reused by any other pointer in the program until it is properly released), then instead of attempting to dereference the resulting pointer, the recommended solution is to declare ptr
as a void *
, which cannot be dereferenced by a conforming implementation.
...
Reading previously dynamically allocated memory after it has been deallocated can lead to abnormal program termination and denial-of-service attacks. Writing memory that has been deallocated can lead to the execution of arbitrary code with the permissions of the vulnerable process.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
MEM50-CPP | High | Likely | Medium | P18 | L1 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Astrée |
| dangling_pointer_use | |||||||
Axivion Bauhaus Suite |
| CertC++-MEM50 | |||||||
Clang |
| clang-analyzer-cplusplus.NewDelete | Checked by clang-tidy , but does not catch all violations of this rule. | ||||||
CodeSonar |
| ALLOC.UAF | Use after free | ||||||
Compass/ROSE |
| USE_AFTER_FREE | Can detect the specific instances where memory is deallocated more than once or read/written to the target of a freed pointer | |||||||
Helix QAC |
5.0
Double Free
| C++4303, C++4304 |
Klocwork |
| UFM.DEREF.MIGHT UFM.DEREF.MUST UFM. |
FFM.MIGHT UFM. |
FFM.MUST UFM.RETURN.MIGHT UFM.RETURN.MUST UFM.USE.MIGHT UFM.USE. |
MUST | |||||||||
LDRA tool suite |
| 483 S, 484 S | Partially implemented | ||||||
Parasoft C/C++test |
| CERT_CPP-MEM50-a | Do not use resources that have been freed | |||||||
Parasoft Insure++ | Runtime detection | ||||||||
Polyspace Bug Finder |
| CERT C++: MEM50-CPP | Checks for:
Rule partially covered. | ||||||
PVS-Studio |
| V586, V774 |
Splint |
|
Related Vulnerabilities
VU#623332 describes VU#623332 describes a double-free vulnerability in the MIT Kerberos 5 function krb5_recvauth() [VU# 623332].
Search for other vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
SEI CERT C++ Coding Standard | |
SEI CERT C Coding Standard | MEM30-C. Do not access freed memory |
MITRE CWE |
Bibliography
[ISO/IEC 14882-2014] | Subclause 3.7.4.1, "Allocation Functions" Subclause 3.7.4.2, "Deallocation Functions" |
[Seacord 2013b] | Chapter 4, "Dynamic Memory Management" |
...
...