Thursday, October 2, 2008

C Memory Leaks and Memory Corruption

Memory errors:
• Heap memory errors:
  • Attempting to free memory already freed.
  • Freeing memory that was not allocated.
  • Attempting to write to memory already freed.
  • Attempting to write to memory which was never allocated.
  • Memory allocation error.
  • Reading/writing to memory out of the bounds of a dynamically allocated array
• stack (local variables) memory errors:
  • Reading/writing to memory out of the bounds of a static array. (array index overflow - index too large/underflow - negative index)
  • Function pointer corruption: Invalid passing of function pointer and thus a bad call to a function.
Memory Leaks:
Memory leak description: Memory is allocated but not released causing an application to consume memory reducing the available memory for other applications and eventually causing the system to page virtual memory to the hard drive slowing the application or crashing the application when than the computer memory resource limits are reached. The system may stop working as these limits are approached.
Many C library functions malloc's memory which MUST be freed: i.e.: strdup(),

#include
#include
...
char *oldString = "Old String";
char newStrig = strdup(oldString);
if(newString == ENOMEM) ... // Fail!!!!
...
free(newString);

Note: You can NOT use the C++ delete call. The strdup() function is part of the C library and you must use free().
Any routine which is supplied by the C libraries or ones written within an application which allocate memory must have the memory freed. Comments on this need should be included in the include file to make users of the function aware of their duties to free the memory and the mechanism by which it is to be freed (free() or delete).
Programmer must free() malloc()'ed memory:
Also for calloc(), malloc() and realloc();

#include
char *textString = malloc(128*sizeof(char));
if(textString == ENOMEM) ... // Fail!!!!
...
free(textString); // Don't free if allocation failed


Check for memory allocation errors. Can't free it if it didn't get allocated.
Pointer re-assignment error leads to dangling pointer:
If the pointer is re-assigned a new value before being freed, it will lead to a "dangling pointer" and memory leak.

Example:
char *a = malloc(128*sizeof(char));
char *b = malloc(128*sizeof(char));
b = a;
free(a);
free(b); // will not free the pointer to the original allocated memory.

Good practice: Use assert to check pointers before freeing or using:
assert(ptr !=0)

Memory Corruption:
Memory Corruption: Memory when altered without an explicit assignment due to the inadvertent and unexpected altering of data held in memory or the altering of a pointer to a specific place in memory.

Buffer overflow:
Example 1:

Overwrite beyond allocated length - overflow.
char *a = malloc(128*sizeof(char));
memcpy(a, data, dataLen); // Error if dataLen too long.
Example 2:
Index of array out of bounds: (array index overflow - index too large/underflow - negative index)
ptr = (char *) malloc(strlen(string_A)); // Should be (string_A + 1) to account for null termination.
strcpy(ptr, string_A); // Copies memory from string_A which is one byte longer than its destination ptr.
Overflow by one byte.

Using an address before memory is allocated and set:
struct *ABC_ptr;
x = ABC_ptr->name;
In this case the memory location is NULL or random.

Using a pointer which is already freed:
char *a = malloc(128*sizeof(char));
..
free(a);
cout <<>
... Do stuff.
Probable overwriting of freed memory.
cout <<> //stuff.

Freeing memory which has already been freed.
Freeing a pointer twice:
char *a = malloc(128*sizeof(char));
free(a);
... Do stuff
free(a); // A check for NULL would indicate nothing.
// This memory space may be reallocated and thus we may be freeing
// memory we do not intend to free or portions of another block of
// memory. The size of the block of memory allocated is often held
// just before the memory block itself..

Freeing memory which was not dynamically allocated:
struct ABC abc;
struct ABC *abc_ptr = &abc;
... free(abc_ptr);


Pointer persistence:
Function returning a pointer from the stack which can get overwritten by the calling function (in this case main()):
int *get_ii()
{
int ii; // Local stack variable
ii = 2;
return ⅈ
}

main()
{
int *ii;
ii = get_ii(); // After this call the stack is given up by the routine
// get_ii() and its values are no longer safe.
... Do stuff
.. ii may be corrupt by this point.
}

This is also true for a local variable within the scope of only { and } as well as for the scope of a function.

Incorrect passing of a function argument:
If the pointer is passed around as an argument and does not get passed correctly, one may try to free the incorrect pointer.