ISO/IEC TS 17961 [fileclose]
Failing to close files or free dynamic memory when they are no longer needed
Description
Rule Definition
Failing to close files or free dynamic memory when they are no longer needed.1
Polyspace Implementation
This checker checks for these issues:
Memory leak.
Resource leak.
Thread-specific memory leak.
Examples
Memory leak
Memory leak 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 */
}
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); }
Resource leak
Resource leak occurs when you
open a file stream by using a FILE
pointer but
do not close it before:
The end of the pointer’s scope.
Assigning the pointer to another stream.
If you do not release file handles explicitly as soon as possible, a failure can occur due to exhaustion of resources.
Close a FILE
pointer before the end of its
scope, or before you assign the pointer to another stream.
FILE
Pointer Not Released Before End of
Scope#include <stdio.h>
void func1( void ) {
FILE *fp1;
fp1 = fopen ( "data1.txt", "w" );
fprintf ( fp1, "*" );
fp1 = fopen ( "data2.txt", "w" );
fprintf ( fp1, "!" );
fclose ( fp1 );
}
In this example, the file pointer fp1
is
pointing to a file data1.txt
. Before fp1
is
explicitly dissociated from the file stream of data1.txt
,
it is used to access another file data2.txt
.
FILE
PointerOne possible correction is to explicitly dissociate fp1
from
the file stream of data1.txt
.
#include <stdio.h> void func1( void ) { FILE *fp1; fp1 = fopen ( "data1.txt", "w" ); fprintf ( fp1, "*" ); fclose(fp1); fp1 = fopen ( "data2.txt", "w" ); fprintf ( fp1, "!" ); fclose ( fp1 ); }
Thread-specific memory leak
Thread-specific memory leak 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; /* Report error */ } print_data(); 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; }
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; /* Report error */
}
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
Decidability: Undecidable |
Version History
Introduced in R2019a
1 Extracts from the standard "ISO/IEC TS 17961 Technical Specification - 2013-11-15" are reproduced with the agreement of AFNOR. Only the original and complete text of the standard, as published by AFNOR Editions - accessible via the website www.boutique.afnor.org - has normative value.
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 (한국어)