There are two forms of Memory accessible to the programmer:
- User’s virtual memory space in which application is run.
- Register memory.
The most obvious memory errors result in a “Segmentation violation” message. The following errors discussed are the not so obvious errors.
- Heap memory errors
- Attempting to free memory already freed.
- Freeing memory that was not allocated.
- 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
If a 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. Some scenario leading to memory leak
- Not freeing malloc()’ed memory
char *textString = malloc(128*sizeof(char)); // Allocation failed if(null == textString) // Do something // Free if allocation successed free(textString);
- Inheritance, polymorphism and the wrong delete
If you are counting on the destructor to delete memory allocated in the constructor beware of this mistake as it will cause a memory leak. Use a virtual destructor to avoid this problem. The ~BaseClass() destructor is called and then the destructor ~DerivedClass() is chosen and called at run time because it is a virtual destructor. If it is not declared virtual then only the ~BaseClass() destructor is called leaving any allocated memory from the DerivedClass object to persist and leak.// Allowed due to polymorphism. BaseClass* obj_ptr = new DerivedClass; // this will call the destructor ~Parent() and NOT ~Child() delete obj_ptr;
- Pointer re-assignment error leads to dangling pointer
char *a = malloc(128*sizeof(char)); char *b = malloc(128*sizeof(char)); b = a; free(a); // will not free the pointer to the original allocated memory. free(b);
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. Memory corruption can set unpredictable values to next memory elements; it can set invalid pointer values; and worst of all, it can corrupt dynamic memory allocator thus causing crash/core-dump of the application/process. Memory corruption may occur because of poor array buffer handling or some abnormal runtime use-cases. Some scenario leading to memory corruption
- Buffer overflow
// Overwrite beyond allocated length - overflow. char *a = malloc(128*sizeof(char)); memcpy(a, data, dataLen); // Error if dataLen too long. // Overflow by one byte. 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.
- Using an address before memory is allocated and set
struct *ABC_ptr; x = ABC_ptr->name;
Stack Corruption
Stack corruption is a phenomenon in which some memory locations at stack are accessed unintentionally due to wrong coding leading to change in values at those memory locations. Since the data corruption happens on stack memory locations, hence the term Stack Corruption. Few ways in which stack corruption may occur :
- When due to some weirdly written code, all the stack memory gets eaten up
- Accessing array out of bounds
- An undefined/freed pointer pointing or storing a garbage stack address.
- When due to some reason, the return address for a function call gets corrupted.
Consuming all the stack memory
In the below piece of code, we declare a variable ‘a’ inside main() and recursively call main() again and again. Now since every time main() gets called, a memory chunk on stack is allocated for main() again and again.
#include<stdio.h> int main(void) { int a=0; a += 1; main(); return 0; }
Upon execution above program gives a segmentation fault/crash. This happened as all the stack memory is consumed and when the main() is called again, there is no more stack memory available.
Accessing array out of bounds
In the below code, suppose, somehow if the code tries to change the value kept at a[3] (which is a memory location not to be accessed by the array).
#include<stdio.h> unsigned int count = 1; int main(void) { int b = 10; int a[3]; a[0] = 1; a[1] = 2; a[2] = 3; printf("\n b = %d \n",b); printf("\n address of b = %x, address of a[3] = %x \n",&b, &a[3]); a[3] = 12; printf("\n b = %d \n",b); return 0; } // Output b = 10 address of b = a246661c, address of a[3] = a246661c b = 12
Illegal access of memory by the statement a[3] is done on the memory address where value of ‘b’ is stored. Hence we see that the value of ‘b’ gets changed silently. Imagine the damage this kind of problem can do in a code which has thousands of line of code.