CWE Rule 401
Description
Rule Description
The software does not sufficiently track and release allocated memory after it has been used, which slowly consumes remaining memory.
Polyspace Implementation
The rule checker checks for these issues:
Memory leak
Thread-specific memory leak
Examples
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 defect 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; /* Defect: 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; }
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 flags a defect. Ignore this defect 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; //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. See the highlighted change in the corrected version.
In this corrected version, a defect still appears on the return
statement in the error handling section of func
. The defect cannot occur in
practice because the error handling section is entered only if dynamic memory allocation
fails. Ignore this remaining defect 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; //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 R2023a
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 (한국어)