C++ valgrind alternative for memory bugs

Eljay Love-Jensen eljay@adobe.com
Tue Mar 30 19:44:00 GMT 2004


Someone asked a question recently on how to catch memory bugs.  I don't 
recall if it was for C or C++.

Below is a VERY lightweight alternative to catch a LIMITED class of memory 
management bugs, for C++.

Valgrind will catch more bugs than this, but is not available on all 
platforms.  ElectricFence will also catch these kinds of bugs.

The new will make sure memory is set to known garbage to start with.  The 
delete will make sure the memory is cleared to different known garbage.  (I 
didn't use the traditional 0xDEADBEEF, because of the potential for 
misaligned memory for MemEnd, which can be fatal (SIGBUS) for certain 
platforms.)

I leave placement-new/delete and nothrow-new/delete as an exercise for the 
reader.

--Eljay

- - - - - - - - - - - - - - - - - - - - - - - - -

#include <cstdlib>
     // malloc, free

#include <cstring>
     // memset

#include <iostream>
     using std::cerr;
     using std::endl;

#include <new>

struct MemBegin
{
     size_t length;
     char beginMark[8]; // B1 or BA
};

struct MemEnd
{
     char endMark[8]; // E1 or EA
};

void* operator new(size_t s) throw(std::bad_alloc)
{
     const size_t supersize = sizeof(MemBegin) + s + sizeof(MemEnd);
     char* p = (char*)malloc(supersize);

     if(p == NULL)
         throw std::bad_alloc();

     memset(p, 0xB1, sizeof(MemBegin)); // B1 for beginning of 1 item
     memset(p + sizeof(MemBegin), 0xA1, s); // A1 for alloc 1 item
     memset(p + sizeof(MemBegin) + s, 0xE1, sizeof(MemEnd)); // E1 for end 
of 1 item

     (*(MemBegin*)p).length = s;

     return (void*)(p + sizeof(MemBegin));
}

void* operator new[](size_t s) throw(std::bad_alloc)
{
     const size_t supersize = sizeof(MemBegin) + s + sizeof(MemEnd);
     char* p = (char*)malloc(supersize);

     if(p == NULL)
         throw std::bad_alloc();

     memset(p, 0xBA, sizeof(MemBegin)); // BA for beginning of array
     memset(p + sizeof(MemBegin), 0xAA, s); // AA for alloc array
     memset(p + sizeof(MemBegin) + s, 0xEA, sizeof(MemEnd)); // EA for end 
of array

     (*(MemBegin*)p).length = s;

     return (void*)(p + sizeof(MemBegin));
}

static void Verify(char* p, char beginMark, char endMark, char freeMark)
{
     MemBegin* b = (MemBegin*)p;

     if(b->beginMark[0] != beginMark
     || b->beginMark[1] != beginMark
     || b->beginMark[2] != beginMark
     || b->beginMark[3] != beginMark
     || b->beginMark[4] != beginMark
     || b->beginMark[5] != beginMark
     || b->beginMark[6] != beginMark
     || b->beginMark[7] != beginMark
     )
     {
         if(beginMark == (char)0xB1)
             cerr << "BeginMark single item: verification failure!";
         else if(beginMark == (char)0xBA)
             cerr << "BeginMark array: verification failure!";
         else
             cerr << "BeginMark (unexpected): verification failure!";

         // We do not trust this de-allocation!
         return;
     }

     MemEnd* e = (MemEnd*)(p + sizeof(MemEnd) + b->length);

     if(e->endMark[0] != endMark
     || e->endMark[1] != endMark
     || e->endMark[2] != endMark
     || e->endMark[3] != endMark
     || e->endMark[4] != endMark
     || e->endMark[5] != endMark
     || e->endMark[6] != endMark
     || e->endMark[7] != endMark
     )
     {
         if(endMark == (char)0xE1)
             cerr << "EndMark single item: verification failure!";
         else if(endMark == (char)0xEA)
             cerr << "EndMark array: verification failure!";
         else
             cerr << "EndMark (unexpected): verification failure!";
         return;
     }

     size_t s = sizeof(MemBegin) + b->length + sizeof(MemEnd);

     memset(p, freeMark, s);
}

void operator delete(void* p) throw()
{
     if(p != NULL)
     {
         Verify((char*)p, 0xB1, 0xE1, 0xF1);
         free(p);
     }
}

void operator delete[](void* p) throw()
{
     if(p != NULL)
     {
         Verify((char*)p, 0xBA, 0xEA, 0xFA);
         free(p);
     }
}



More information about the Gcc-help mailing list