CWE Rule 404
Description
Rule Description
The product does not release or incorrectly releases a resource before it is made available for re-use.
Polyspace Implementation
The rule checker checks for these issues:
Invalid deletion of pointer
Invalid free of pointer
Memory leak
Mismatched alloc/dealloc functions on Windows
Thread-specific memory leak
Examples
Invalid deletion of pointer
This issue occurs when:
You release a block of memory with the
delete
operator but the memory was previously not allocated with thenew
operator.You release a block of memory with the
delete
operator using the single-object notation but the memory was previously allocated as an array with thenew
operator.
This issue applies only to C++ source files.
The risk depends on the cause of the issue:
The
delete
operator releases a block of memory allocated on the heap. If you try to access a location on the heap that you did not allocate previously, a segmentation fault can occur.If you use the single-object notation for
delete
on a pointer that is previously allocated with the array notation fornew
, the behavior is undefined.
The issue can also highlight other coding errors. For instance, you perhaps wanted
to use the delete
operator or a previous new
operator on a different pointer.
The fix depends on the cause of the issue:
In most cases, you can fix the issue by removing the
delete
statement. If the pointer is not allocated memory from the heap with thenew
operator, you do not need to release the pointer withdelete
. You can simply reuse the pointer as required or let the object be destroyed at the end of its scope.In case of mismatched notation for
new
anddelete
, correct the mismatch. For instance, to allocate and deallocate a single object, use this notation:classType* ptr = new classType; delete ptr;
To allocate and deallocate an array objects, use this notation:
classType* p2 = new classType[10]; delete[] p2;
If the issue highlights a coding error such as use of delete
or
new
on the wrong pointer, correct the error.
void assign_ones(void) { int ptr[10]; for(int i=0;i<10;i++) *(ptr+i)=1; delete[] ptr; //Noncompliant }
The pointer ptr
is released
using the delete
operator. However, ptr
points
to a memory location that was not dynamically allocated.
If the number of elements of the array ptr
is
known at compile time, one possible correction is to remove the deallocation
of the pointer ptr
.
void assign_ones(void) { int ptr[10]; for(int i=0;i<10;i++) *(ptr+i)=1; }
If the number of array elements is not known
at compile time, one possible correction is to dynamically allocate
memory to the array ptr
using the new
operator.
void assign_ones(int num) { int *ptr = new int[num]; for(int i=0; i < num; i++) *(ptr+i) = 1; delete[] ptr; }
int main (void) { int *p_scale = new int[5]; //more code using scal delete p_scale; //Noncompliant }
In this example, p_scale
is initialized to
an array of size 5 using new int[5]
. However, p_scale
is
deleted with delete
instead of delete[]
.
The new
-delete
pair does not
match. Do not use delete
without the brackets when
deleting arrays.
delete
to new
One possible correction is to add brackets so the delete
matches
the new []
declaration.
int main (void) { int *p_scale = new int[5]; //more code using p_scale delete[] p_scale; }
new
to delete
Another possible correction is to change the declaration of p_scale
.
If you meant to initialize p_scale
as 5 itself
instead of an array of size 5, you must use different syntax. For
this correction, change the square brackets in the initialization
to parentheses. Leave the delete
statement as it
is.
int main (void) { int *p_scale = new int(5); //more code using p_scale delete p_scale; }
Invalid free of pointer
This issue occurs
when a block of memory released using the free
function
was not previously allocated using malloc
, calloc
,
or realloc
.
The free
function releases a block of memory allocated on the
heap. If you try to access a location on the heap that you did not allocate
previously, a segmentation fault can occur.
The issue can highlight coding errors. For instance, you perhaps wanted to use the
free
function or a previous malloc
function on a different pointer.
In most cases, you can fix the issue by removing the free
statement. If the pointer is not allocated memory from the heap with
malloc
or calloc
, you do not need to free
the pointer. You can simply reuse the pointer as required.
If the issue highlights a coding error such as use of free
or
malloc
on the wrong pointer, correct the error.
If the issue occurs because you use the free
function to free
memory allocated with the new
operator, replace the
free
function with the delete
operator.
#include <stdlib.h> void Assign_Ones(void) { int p[10]; for(int i=0;i<10;i++) *(p+i)=1; free(p); //Noncompliant /* Violation: p does not point to dynamically allocated memory */ }
The pointer p
is deallocated
using the free
function. However, p
points
to a memory location that was not dynamically allocated.
If the number of elements of the array p
is
known at compile time, one possible correction is to remove the deallocation
of the pointer p
.
#include <stdlib.h> void Assign_Ones(void) { int p[10]; for(int i=0;i<10;i++) *(p+i)=1; /* Fix: Remove deallocation of p */ }
If the number of elements of the array p
is
not known at compile time, one possible correction is to dynamically
allocate memory to the array p
.
#include <stdlib.h> void Assign_Ones(int num) { int *p; /* Fix: Allocate memory dynamically to p */ p=(int*) calloc(10,sizeof(int)); for(int i=0;i<10;i++) *(p+i)=1; free(p); }
Memory leak
This issue occurs when you do not free a block of memory
allocated through malloc
, calloc
,
realloc
, or new
. If the memory is allocated in a
function, the issue does not occur if:
Within the function, you free the memory using
free
ordelete
.The function returns the pointer assigned by
malloc
,calloc
,realloc
, ornew
.The function stores the pointer in a global variable or in a parameter.
Dynamic memory allocation functions such as malloc
allocate
memory on the heap. If you do not release the memory after use, you reduce the
amount of memory available for another allocation. On embedded systems with limited
memory, you might end up exhausting available heap memory even during program
execution.
Determine the scope where the dynamically allocated memory is accessed. Free the memory block at the end of this scope.
To free a block of memory, use the free
function on the pointer
that was used during memory allocation. For
instance:
ptr = (int*)malloc(sizeof(int)); ... free(ptr);
It is a good practice to allocate and free memory in the same module at the same
level of abstraction. For instance, in this example, func
allocates and frees memory at the same level but func2
does
not.
void func() { ptr = (int*)malloc(sizeof(int)); { ... } free(ptr); } void func2() { { ptr = (int*)malloc(sizeof(int)); ... } free(ptr); }
#include<stdlib.h> #include<stdio.h> void assign_memory(void) { int* pi = (int*)malloc(sizeof(int)); if (pi == NULL) { printf("Memory allocation failed"); return; } *pi = 42; /* Violation: pi is not freed */ } //Noncompliant
In this example, pi
is dynamically
allocated by malloc
. The function assign_memory
does
not free the memory, nor does it return pi
.
One possible correction is to free the memory
referenced by pi
using the free
function.
The free
function must be called before the function assign_memory
terminates
#include<stdlib.h> #include<stdio.h> void assign_memory(void) { int* pi = (int*)malloc(sizeof(int)); if (pi == NULL) { printf("Memory allocation failed"); return; } *pi = 42; /* Fix: Free the pointer pi*/ free(pi); }
Another possible correction is to return the
pointer pi
. Returning pi
allows
the function calling assign_memory
to free the
memory block using pi
.
#include<stdlib.h> #include<stdio.h> int* assign_memory(void) { int* pi = (int*)malloc(sizeof(int)); if (pi == NULL) { printf("Memory allocation failed"); return(pi); } *pi = 42; /* Fix: Return the pointer pi*/ return(pi); }
#define NULL '\0' void initialize_arr1(void) { int *p_scalar = new int(5); } //Noncompliant void initialize_arr2(void) { int *p_array = new int[5]; } //Noncompliant
In this example, the functions create two variables, p_scalar
and p_array
,
using the new
keyword. However, the functions end
without cleaning up the memory for these pointers. Because the functions
used new
to create these variables, you must clean
up their memory by calling delete
at the end of
each function.
To correct this error, add a delete
statement
for every new
initialization. If you used brackets []
to
instantiate a variable, you must call delete with brackets as well.
#define NULL '\0' void initialize_arrs(void) { int *p_scalar = new int(5); int *p_array = new int[5]; delete p_scalar; p_scalar = NULL; delete[] p_array; p_scalar = NULL; }
Mismatched alloc/dealloc functions on Windows
This issue occurs when you use a Windows® deallocation function that is not properly paired to its corresponding allocation function.
Deallocating memory with a function that does not match the allocation function can cause memory corruption or undefined behavior. If you are using an older version of Windows, the improper function can also cause compatibility issues with newer versions.
Properly pair your allocation and deallocation functions according to the functions listed in this table.
Allocation Function | Deallocation Function |
---|---|
malloc() | free() |
realloc() | free() |
calloc() | free() |
_aligned_malloc() | _aligned_free() |
_aligned_offset_malloc() | _aligned_free() |
_aligned_realloc() | _aligned_free() |
_aligned_offset_realloc() | _aligned_free() |
_aligned_recalloc() | _aligned_free() |
_aligned_offset_recalloc() | _aligned_free() |
_malloca() | _freea() |
LocalAlloc() | LocalFree() |
LocalReAlloc() | LocalFree() |
GlobalAlloc() | GlobalFree() |
GlobalReAlloc() | GlobalFree() |
VirtualAlloc() | VirtualFree() |
VirtualAllocEx() | VirtualFreeEx() |
VirtualAllocExNuma() | VirtualFreeEx() |
HeapAlloc() | HeapFree() |
HeapReAlloc() | HeapFree() |
#ifdef _WIN32_ #include <windows.h> #else #define _WIN32_ typedef void *HANDLE; typedef HANDLE HGLOBAL; typedef HANDLE HLOCAL; typedef unsigned int UINT; extern HLOCAL LocalAlloc(UINT uFlags, UINT uBytes); extern HLOCAL LocalFree(HLOCAL hMem); extern HGLOBAL GlobalFree(HGLOBAL hMem); #endif #define SIZE9 9 void func(void) { /* Memory allocation */ HLOCAL p = LocalAlloc(0x0000, SIZE9); if (p) { /* Memory deallocation. */ GlobalFree(p); //Noncompliant } }
In this example, memory is allocated with LocallAlloc()
. The program
then erroneously uses GlobalFree()
to deallocate the memory.
When you allocate memory with LocalAllocate()
, use
LocalFree()
to deallocate the memory.
#ifdef _WIN32_ #include <windows.h> #else #define _WIN32_ typedef void *HANDLE; typedef HANDLE HGLOBAL; typedef HANDLE HLOCAL; typedef unsigned int UINT; extern HLOCAL LocalAlloc(UINT uFlags, UINT uBytes); extern HLOCAL LocalFree(HLOCAL hMem); extern HGLOBAL GlobalFree(HGLOBAL hMem); #endif #define SIZE9 9 void func(void) { /* Memory allocation */ HLOCAL p = LocalAlloc(0x0000, SIZE9); if (p) { /* Memory deallocation. */ LocalFree(p); } }
Thread-specific memory leak
This checker is deactivated in a default Polyspace® as You Code analysis. See Checkers Deactivated in Polyspace as You Code Analysis (Polyspace Access).
This issue occurs when you do not free thread-specific dynamically allocated memory before the end of a thread.
To create thread-specific storage, you generally do these steps:
You create a key for thread-specific storage.
You create the threads.
In each thread, you allocate storage dynamically and then associate the key with this storage.
After the association, you can read the stored data later using the key.
Before the end of the thread, you free the thread-specific memory using the key.
The checker flags execution paths in the thread where the last step is missing.
The checker works on these families of functions:
tss_get
andtss_set
(C11)pthread_getspecific
andpthread_setspecific
(POSIX)
The data stored in the memory is available to other processes even after the threads end (memory leak). Besides security vulnerabilities, memory leaks can shrink the amount of available memory and reduce performance.
Free dynamically allocated memory before the end of a thread.
You can explicitly free dynamically allocated memory with functions such as
free
.
Alternatively, when you create a key, you can associate a destructor function with the key. The destructor function is called with the key value as argument at the end of a thread. In the body of the destructor function, you can free any memory associated with the key. If you use this method, Bug Finder still reports a violation. Ignore this violation with appropriate comments. See:
Address Results in Polyspace User Interface Through Bug Fixes or Justifications if you review results in the Polyspace user interface.
Address Results in Polyspace Access Through Bug Fixes or Justifications (Polyspace Access) if you review results in a web browser.
Annotate Code and Hide Known or Acceptable Results if you review results in an IDE.
#include <threads.h> #include <stdlib.h> /* Global key to the thread-specific storage */ tss_t key; //Testing enum { MAX_THREADS = 3 }; int add_data(void) { int *data = (int *)malloc(2 * sizeof(int)); if (data == NULL) { return -1; /* Report error */ } data[0] = 0; data[1] = 1; if (thrd_success != tss_set(key, (void *)data)) { /* Handle error */ } return 0; } void print_data(void) { /* Get this thread's global data from key */ int *data = tss_get(key); if (data != NULL) { /* Print data */ } } int func(void *dummy) { if (add_data() != 0) { return -1; /* Report error */ //Noncompliant } print_data(); return 0; //Noncompliant } int main(void) { thrd_t thread_id[MAX_THREADS]; /* Create the key before creating the threads */ if (thrd_success != tss_create(&key, NULL)) { /* Handle error */ } /* Create threads that would store specific storage */ for (size_t i = 0; i < MAX_THREADS; i++) { if (thrd_success != thrd_create(&thread_id[i], func, NULL)) { /* Handle error */ } } for (size_t i = 0; i < MAX_THREADS; i++) { if (thrd_success != thrd_join(thread_id[i], NULL)) { /* Handle error */ } } tss_delete(key); return 0; }
In this example, the start function of each thread func
calls two functions:
add_data
: This function allocates storage dynamically and associates the storage with a key using thetss_set
function.print_data
: This function reads the stored data using thetss_get
function.
At the points where func
returns, the dynamically allocated storage
has not been freed.
One possible correction is to free dynamically allocated memory explicitly before leaving the start function of a thread.
In this corrected version, a violation still
appears on the return
statement in the error handling section of
func
. The violation cannot occur in practice because the error
handling section is entered only if dynamic memory allocation fails. Ignore this remaining
violation with appropriate comments. See:
Address Results in Polyspace User Interface Through Bug Fixes or Justifications if you review results in the Polyspace user interface.
Address Results in Polyspace Access Through Bug Fixes or Justifications (Polyspace Access) if you review results in a web browser.
Annotate Code and Hide Known or Acceptable Results if you review results in an IDE.
#include <threads.h> #include <stdlib.h> /* Global key to the thread-specific storage */ tss_t key; enum { MAX_THREADS = 3 }; int add_data(void) { int *data = (int *)malloc(2 * sizeof(int)); if (data == NULL) { return -1; /* Report error */ } data[0] = 0; data[1] = 1; if (thrd_success != tss_set(key, (void *)data)) { /* Handle error */ } return 0; } void print_data(void) { /* Get this thread's global data from key */ int *data = tss_get(key); if (data != NULL) { /* Print data */ } } int func(void *dummy) { if (add_data() != 0) { return -1; /* Report error */ //Noncompliant } print_data(); free(tss_get(key)); return 0; } int main(void) { thrd_t thread_id[MAX_THREADS]; /* Create the key before creating the threads */ if (thrd_success != tss_create(&key, NULL)) { /* Handle error */ } /* Create threads that would store specific storage */ for (size_t i = 0; i < MAX_THREADS; i++) { if (thrd_success != thrd_create(&thread_id[i], func, NULL)) { /* Handle error */ } } for (size_t i = 0; i < MAX_THREADS; i++) { if (thrd_success != thrd_join(thread_id[i], NULL)) { /* Handle error */ } } tss_delete(key); return 0; }
Check Information
Category: Others |
Version History
Introduced in R2024a
See Also
External Websites
MATLAB Command
You clicked a link that corresponds to this MATLAB command:
Run the command by entering it in the MATLAB Command Window. Web browsers do not support MATLAB commands.
Select a Web Site
Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .
You can also select a web site from the following list
How to Get Best Site Performance
Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.
Americas
- América Latina (Español)
- Canada (English)
- United States (English)
Europe
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)
Asia Pacific
- Australia (English)
- India (English)
- New Zealand (English)
- 中国
- 日本Japanese (日本語)
- 한국Korean (한국어)