Thursday, May 24, 2007

some c code to debug

...dumping an old post from my older WordPress blog:

I stumbled upon http://www.advagato.org today and spent a few minutes trying to solve this “spot the bug” quiz.

According to the person who posted it, the challenge is to find atleast 2 memory-related bugs in the code (potential memory leak and/or a dangling pointer creation) in the mydata_add() function:

typedef struct {
int count; // number of items in each array
Foo* foos; // array of Foos
Bar* bars; // array of Bars
} MyData;

Now, here’s the function used to add a (foo,bar) pair to the data structure:

static int
mydata_add( MyData *data, Foo foo, Bar bar )
{
Foo* new_foos;
Bar* new_bars;
int count = data->count;
 if ( count == 0 )
  new_foos = malloc( sizeof(Foo) );
else
new_foos = realloc( data->foos, sizeof(Foo)*(count+1) );
if ( new_foos == NULL )
return -1;
if ( count == 0 )
new_bars = malloc( sizeof(Bar) );
else
new_bars = realloc( data->bars, sizeof(Bar)*(count+1) );
if ( new_bars == NULL )
return -1;
new_foos[count] = foo;
new_bars[count] = bar;
data->foos = new_foos;
data->bars = new_bars;
data->count = count+1;
return 0;
}

Answer:
First, I had to figure out what realloc() did. According to the man page:

void *realloc(void *ptr, size_t size);

realloc() changes the size of the memory block pointed to by ptr to
size bytes. The contents will be unchanged to the minimum of the old
and new sizes; newly allocated memory will be uninitialized. If ptr is
NULL, the call is equivalent to malloc(size); if size is equal to zero,
the call is equivalent to free(ptr). Unless ptr is NULL, it must have
been returned by an earlier call to malloc(), calloc() or realloc().
As for the answer to the original question:

The two memory errors was basically of the same type. It happens when the second allocation fails (either malloc() or realloc()):
1. In the case when count was 0, it would leak a newly allocated (malloc()) block of memory.
2. In the case when count was > 0, it would have reallocated the block pointed by data->foos, but would not have updated the corresponding pointer. Now in this memory block would not be in the same position as the original one (before the realloc()), which would be more or less the case, we would have created a dangling pointer.

No comments: