As noted in under undefined behavior 179 of in Annex J of the C Standard [ISO/IEC 9899:2011], the behavior of a program is undefined when
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h>
int f(size_t n) {
int error_condition = 0;
int *x = (int *)malloc(n * sizeof(int));
if (x == NULL)
return -1;
/* Use x and set error_condition on error. */
if (error_condition == 1) {
/* Handle error */
free(x);
}
free(x);
return error_condition;
}
|
...
In this compliant solution, the memory referenced by x
is freed only freed once. This is accomplished by eliminating the call to free()
when error_condition
is set:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> int f(size_t n) { int error_condition = 0; if (n > SIZE_MAX / sizeof(int)) { return -1; } int *x = (int*)malloc(n * sizeof(int)); if (x == NULL) { /* Report allocation failure to caller. */ return -1; } /* Use x and set error_condition on error. */ if (error_condition != 0) { /* Handle error condition and proceed. */ } free(x); x = 0; return error_condition; } |
Note that this solution checks for numeric overflow . (See see INT32-C. Ensure that operations on signed integers do not result in overflow). ) It also complies with MEM01-C. Store a new value in pointers immediately after free().
Noncompliant Code Example (realloc()
)
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> /* p is a pointer to dynamically allocated memory. */ void func(void *p, size_t size) { p2 = realloc(p, size); if (p2 == NULL) { /* p may be indeterminate when (size == 0). */ free(p); return; } } |
Subclause 7.22.3 of the C Standard [ISO/IEC 9899:2011] states:
...
If realloc()
is called with size
equal to 0 , and then if a null pointer is returned, the old value should be unchanged. However, there are some common but nonconforming implementations that free the pointer, including the following:
...
In this compliant solution, allocations of zero 0 bytes are prevented, ensuring that p
is freed exactly once:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h>
/* p is a pointer to dynamically allocated memory. */
void func(void *p, size_t size) {
if (size) {
p2 = realloc(p, size);
if (p2 == NULL) {
free(p);
return;
}
} else {
free(p);
return;
}
} |
...
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
MEM31-C | highHigh | probableProbable | mediumMedium | P12 | L1 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
| RESOURCE_LEAK USE_AFTER_FREE | Finds resource leaks from variables that go out of scope while owning a resource Can find the instances where a freed memory is freed again. Coverity Prevent cannot discover all violations of this rule, so further verification is necessary | |||||||
5.0 | Double Free | ||||||||
| MLK | ||||||||
| 484 S | Fully implemented | |||||||
|
...
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
CERT C Secure Coding Standard | MEM01-C. Store a new value in pointers immediately after free() MEM04-C. Do not perform zero-length allocations INT32-C. Ensure that operations on signed integers do not result in overflow |
CERT C++ Secure Coding Standard | MEM31-CPP. Free dynamically allocated memory exactly once |
ISO/IEC TR 24772:2013 | Dangling Reference to Heap [XYK] Memory Leak [XYL] |
ISO/IEC TS 17961 | Freeing memory multiple times [dblfree] |
MITRE CWE | CWE-415, Double free |
Bibliography
[ISO/IEC 9899:2011] | Subclause 7.22.3, "Memory Management Functions" Annex J, J.2, "Undefined behaviorBehavior" |
[MIT 2005] | |
[OWASP Double Free] | "Double Free" |
[Viega 2005] | "Doubly Freeing Memory" |
[VU#623332] |
...