It may seem that when dynamic memory has been deallocated, we are done with it. We have avoided a memory leak and can confidently move on to other issues. In some cases this may be the case. However, we may need to be concerned with issues such as how we handle sensitive data and whether we need to even worry about freeing memory if the application is about to terminate. In this discussion we will examine these issues and others.
The Heap and System Memory
The heap typically uses operating system functions to manage its memory. The heap’s size may be fixed when the program is created, or it may be allowed to grow. However, the heap manager does not necessarily return memory to the operating system when the
free function is called. The deallocated memory is simply made available for subsequent use by the application. Thus, when a program allocates and then frees up memory, the deallocation of memory is not normally reflected in the application’s memory usage as seen from the operating system perspective.
Memory is typically deallocated using the free function. One concern deals with what happens when we try to free the same memory twice. Freeing a block of memory twice is referred to as double free. The following illustrates how this can be done:
char *name = (char*)malloc(...); ... free(name); // First free ... free(name); // Double free
More subtle occurrences of double free occur when pointers are aliased:
char *name = (char*)malloc(...); char *tmp = name; ... free(name); // First free ... free(tmp); // Double free
In an earlier version of the zlib compression library, it was possible for a double-free operation to result in a denial of service attack or possibly to insert code into the program. However, this is extremely unlikely and the vulnerability has been addressed in newer releases of the library. More information about this vulnerability can be found at cert.org.
A simple technique to avoid this type of vulnerability is to always assign NULL to a pointer after it has been freed. Subsequent attempts to free a null pointer will be ignored by most heap managers.
char *name = (char*)malloc(...); ... free(name); name = NULL;
In Chapter 3 of my book, Understanding and Using C Pointers, I describe how to write your own free function that automatically assigns NULL for the freed pointer. The function is also a good example of using a pointer to a pointer.
Clearing Sensitive Data
It is a good idea to overwrite sensitive data in memory once it is no longer needed. When your application terminates, most operating systems do not zero out or otherwise manipulate the memory used by your application. Your old space may be allocated to another program, which will have access to its contents. Overwriting sensitive data will make it more difficult for another program to extract useful information from program address space previously used to hold sensitive data. The following sequence illustrates zeroing out of sensitive data in a program:
char name; int userID; char *securityQuestion; // assign values ... // Delete sensitive information memset(name,0,sizeof(name)); userID = 0; memset(securityQuestion,0,strlen(securityQuestion));
If name has been declared as a pointer, then we should clear its memory before we deallocate it, as shown below:
char *name = (char*)malloc(...); ... memset(name,0,sizeof(name)); free(name);
It should be noted that for many operating systems, certified to handle classified data, memory is automatically cleared when the program is no longer using it. This automatic clearing of data incurs additional operating system overhead.
Freeing Memory upon Program Termination
The operating system is responsible for maintaining the resources of an application, including its memory. When an application terminates, it is the operating system’s responsibility to reallocate this memory for other applications. The state of the terminated application’s memory, corrupted or uncorrupted, is not an issue. In fact, one of the reasons an application may terminate is because its memory is corrupted. With an abnormal program termination, cleanup may not be possible.
With this said, there may be other reasons why memory should be freed when a program terminates normally:
- The conscientious programmer may want to free memory as a quality issue. It is always a good habit to free memory after it is no longer needed, even if the application is terminating.
- If you use a tool to detect memory leaks or similar problems, then deallocating memory will clean up the output of such tools.
- In some less complex operating systems, the operating system may not reclaim memory automatically, and it may be the program’s responsibility to reclaim memory before terminating.
- Also, a later version of the application could add code toward the end of the program. If the previous memory has not been freed, problems could arise.
On the other side of the coin, ensuring that all memory is free before program termination:
- May be more trouble than it’s worth
- Can be time consuming and complicated for the deallocation of complex structures
- Can add to the application’s size
- Results in longer running time
- Introduces the opportunity for more programming errors
Whether memory should be deallocated prior to program termination is application-specific.
As you can see, there are many issues that should be considered when freeing up memory. Simply using the free function is not also sufficient. Many of these issues also apply to other memory functions such as the realloc function. Understanding how memory is used in a global context provides insight as to how it should be treated within a program.