Play and learn 300 000+ tabs online

Thursday, May 27, 2010

C/C++ software memory corruption types

C/C++ software memory corruption types:





There are two forms of Linux Memory accessible to the programmer:


  1. User's virtual memory space in which application is run.
  2. Register memory.



The most obvious memory errors result in a "Segmentation violation"
message.
This may alert the programmer to the location of the memory error
when the program is run in gdb.
The following errors discussed are the not so obvious errors.



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(),











01#include <stdlib.h>
02#include
<string.h>
03 
04...
05 
06char *oldString = "Old
String"
;
07char newStrig = strdup(oldString);
08if(newString == ENOMEM) ... // Fail!!!!
09 
10...
11 
12free(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();











1#include <stdlib.h>
2 
3char *textString = malloc(128*sizeof(char));
4if(textString == ENOMEM) ... // Fail!!!!
5...
6free(textString);  //
Don't free if allocation failed






Check for memory allocation errors. Can't free it if it didn't get
allocated.







Programmer must delete new'ed memory:










using namespace std;

ClassTypeA *ptr = new ClassTypeA;

...

delete ptr;






New/delete is preferred to malloc()/free() because it can initialize the

memory and it invokes the constructor for new objects. New/delete also
point to the correct memory type.
Note on mixing source code containing new/delete and malloc()/free().
This is not a problem with the GNU C++ compiler but this is not
guaranteed
for all C++ compilers.






Inheritance, polymorphism and the wrong delete:











1BaseClass* obj_ptr = new
DerivedClass;  // Allowed due to polymorphism.
2...
3delete obj_ptr;  // this
will call the destructor ~Parent() and NOT ~Child()






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 to persist and leak.
This assumes that the DerivedClass has extra memory allocated above and
beyond
that of the BaseClass which must be freed.



The same ill effect can be achieved with a C style cast to a class of
less scope which will dumb down the destructor to that which may not
execute all the freeing of the original class. A C++ style dynamic cast
may
prevent this error as it will recognize the loss of translation and not
allow the cast to take place resulting in a traceable crash rather a
tough
to find memory leak.







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:










1char *a = malloc(128*sizeof(char));
2char *b = malloc(128*sizeof(char));
3b = a;
4free(a);
5free(b); // will not
free the pointer to the original allocated memory.









Default copy constructor may not give correct results:



Memory allocated by copy constructors for pointer duplication.
Check in destructor and delete if necessary.
Memory allocated in passing class by value which invokes copy
constructor.
Also beware, the default copy constructor may not give you the results
you want
especially when dealing with pointers as the default copy constructor
has no
knowledge of how to copy the contents of what the pointer points to.

To prohibit the use of the default copy constructor define a null
assignment operator.










1ClassA& operator=(const
ClassA& right_hand_side);









Good practice:
Use assert to check pointers before freeing or using:










1assert(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.










1char *a = malloc(128*sizeof(char));
2memcpy(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)










1ptr = (char *) malloc(strlen(string_A));  //
Should be (string_A + 1) to account for null termination.
2strcpy(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:










1struct *ABC_ptr;
2x = ABC_ptr->name;





In this case the memory location is NULL or random.






Using a pointer which is already freed:










01char *a = malloc(128*sizeof(char));
02..
03..
04free(a);
05 
06cout << a << endl;  // This will probably work but dangerous.
07 
08... Do stuff. Probable overwriting of freed memory.
09 
10cout << a << endl;  // No longer the same contents. Memory overwritten by new
stuff.









Freeing memory which has already been freed. Also applies to
delete.



Freeing a pointer twice:










1char *a = malloc(128*sizeof(char));
2free(a);
3...   Do stuff
4free(a);  // A check for
NULL would indicate nothing.
5          // This memory space may be reallocated and thus we may be
freeing
6          // memory we
do not intend to free or portions of another block of
7          // memory. The size of the block of memory allocated is often
held
8          // just
before the memory block itself..





Freeing memory which was not dynamically allocated:










1struct ABC abc;
2struct ABC *abc_ptr = &abc;
3...
4free(abc_ptr);









Incorrect use of delete: The delete must match the use of new.



The pairing is new/delete and new [] / delete[]










1ClassABC *abc_ptr = new
ClassABC[100];
2...
3delete [] abc_ptr;





Use of "delete abc_ptr" is an error.


Do not use malloc()/free() with a C++ class as it will not call the
constructor
or destructor. Also malloc()/free() can not be mixed with new/delete.
i.e. Free() can not be used to free memory allocated with new
and delete can not be used to free memory allocated with malloc().







Exception Errors:



Freeing memory never allocated.
If you use a constructor to allocate memory but an exception is thrown
before all is allocated, the destructor needs to be aware that fact or
else
it may try to free memory which was never allocated.



Also the converse is true. If the destructor throws an exception,
subsequent
steps which free memory may not be executed. This applies to the
destructor
and all nested destructors which handle/re-throw the exception while the
stack
unwinds.







Pointer persistence:



Function returning a pointer from the stack which can get overwritten
by the calling function (in this case main()):










01int *get_ii()
02{
03   int ii;        // Local stack variable
04   ii
= 2;
05   return &ii;
06}
07main()
08{
09  int *ii;
10  ii
= get_ii(); 
// After this call the
stack is given up by the routine
11                  // get_ii() and its values are no longer safe.
12 
13  ... Do stuff
14  .. 
ii may be corrupt by
this point.
15}





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.







Mixing the object base class and derived class:



If mixing the object base class and derived class
when passing an object by value as a function parameter, make sure that
you understand what may be lost.










1function_A(BaseClass baseClass_ptr)
2{
3...
4}
5 
6// Call to function
7function_A(derivedClass_ptr); // Note that much of the information contained
8                            // in the derived class will not be passed into
9                            // the function including virtual destructors.









Copying an object:



Don't use memcpy() or any bit for bit copy function to copy an object.
It will not execute the class constructor. What kind of person would do
this??
Passing an object in a va_arg() list will result in a bit for bit copy
and
will not use the default copy constructor.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.