The C Standard, Annex J [ISO/IEC 9899:2011], states that the behavior of a program is undefined when
The pointer argument to the
free
orrealloc
function does not match a pointer earlier returned by a memory management function, or the space has been deallocated by a call tofree
orrealloc
.
See also undefined behavior 179.
Freeing memory that is not allocated dynamically can result in heap corruption and other serious errors. Do not call free()
on a pointer other than one returned by a standard memory allocation function, such as malloc()
, calloc()
, realloc()
, or aligned_alloc()
.
A similar situation arises when realloc()
is supplied a pointer to non-dynamically allocated memory. The realloc()
function is used to resize a block of dynamic memory. If realloc()
is supplied a pointer to memory not allocated by a standard memory allocation function, the behavior is undefined. One consequence is that the program may terminate abnormally.
This rule does not apply to null pointers. The C Standard guarantees that if free()
is passed a null pointer, no action occurs.
Noncompliant Code Example
This noncompliant code example sets c_str
to reference either dynamically allocated memory or a statically allocated string literal depending on the value of argc
. In either case, c_str
is passed as an argument to free()
. If anything other than dynamically allocated memory is referenced by c_str
, the call to free(c_str)
is erroneous.
#include <stdlib.h> #include <string.h> #include <stdio.h> enum { MAX_ALLOCATION = 1000 }; int main(int argc, const char *argv[]) { char *c_str = NULL; size_t len; if (argc == 2) { len = strlen(argv[1]) + 1; if (len > MAX_ALLOCATION) { /* Handle error */ } c_str = (char *)malloc(len); if (c_str == NULL) { /* Handle error */ } strcpy(c_str, argv[1]); } else { c_str = "usage: $>a.exe [string]"; printf("%s\n", c_str); } free(c_str); return 0; }
Compliant Solution
This compliant solution eliminates the possibility of c_str
referencing memory that is not allocated dynamically when passed to free()
:
#include <stdlib.h> #include <string.h> #include <stdio.h> enum { MAX_ALLOCATION = 1000 }; int main(int argc, const char *argv[]) { char *c_str = NULL; size_t len; if (argc == 2) { len = strlen(argv[1]) + 1; if (len > MAX_ALLOCATION) { /* Handle error */ } c_str = (char *)malloc(len); if (c_str == NULL) { /* Handle error */ } strcpy(c_str, argv[1]); } else { printf("%s\n", "usage: $>a.exe [string]"); return EXIT_FAILURE; } free(c_str); return 0; }
Noncompliant Code Example (realloc()
)
In this noncompliant example, the pointer parameter to realloc()
, buf
, does not refer to dynamically allocated memory:
#include <stdlib.h> enum { BUFSIZE = 256 }; void f(void) { char buf[BUFSIZE]; char *p = (char *)realloc(buf, 2 * BUFSIZE); if (p == NULL) { /* Handle error */ } }
Compliant Solution (realloc()
)
In this compliant solution, buf
refers to dynamically allocated memory:
#include <stdlib.h> enum { BUFSIZE = 256 }; void f(void) { char *buf = (char *)malloc(BUFSIZE * sizeof(char)); char *p = (char *)realloc(buf, 2 * BUFSIZE); if (p == NULL) { /* Handle error */ } }
Note that realloc()
will behave properly even if malloc()
failed, because when given a null pointer, realloc()
behaves like a call to malloc()
.
Risk Assessment
The consequences of this error depend on the implementation, but they range from nothing to arbitrary code execution if that memory is reused by malloc()
.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
MEM34-C | High | Likely | Medium | P18 | L1 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Clang | 3.9 | clang-analyzer-unix.Malloc | Checked by clang-tidy ; can detect some instances of this rule, but does not detect all. |
CodeSonar | 8.1p0 | ALLOC.FNH | Free Non-Heap Variable |
Can detect some violations of this rule | |||
2017.07 | BAD_FREE | Identifies calls to | |
2024.3 | FNH.MIGHT | ||
9.7.1 | 407 S, 483 S | Partially implemented |
Related Vulnerabilities
CVE-2015-0240 describes a vulnerability in which an uninitialized pointer is passed to TALLOC_FREE()
, which is a Samba-specific memory deallocation macro that wraps the talloc_free()
function. The implementation of talloc_free()
would access the uninitialized pointer, resulting in a remote exploit.
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
CERT C Secure Coding Standard | MEM31-C. Free dynamically allocated memory when no longer needed |
SEI CERT C++ Coding Standard | MEM51-CPP. Properly deallocate dynamically allocated resources |
ISO/IEC TS 17961 | Reallocating or freeing memory that was not dynamically allocated [xfree] |
MITRE CWE | CWE-590, Free of Memory Not on the Heap |
Bibliography
[ISO/IEC 9899:2011] | J.2, "Undefined Behavior" |
[Seacord 2013b] | Chapter 4, "Dynamic Memory Management" |