tags:

views:

357

answers:

6

How to solve a double free problem by writing a wrapper function with name free so that i need not change every free call in my source code?

+26  A: 

Don't do that.

No, really. Fix the actual problem. Once free() has been called on a pointer, your code should not be holding on to it for any reason. Null it out so you can't free it again; this will make any other problems caused by stale pointer dereferencing visible too.

moonshadow
+1 for setting the pointer to NULL. That's actually the easiest solution to never double-free. In any event, writing a custom `my_free()` that does a `free()` and then sets the pointer to NULL seems to be the easiest way to ensure this. But that involves code changes :-)
Joey
Good point about the wrapper functino that takes care of nulling out the pointer. Some Languages even include these and it is always a good practice to use them instead. Delphi objects have a free() method but it is always better to use the FreeAndNil() global function. Of course it also doesn't throw errors when the pointer is already null which free would.
Chris J
@Chris: what makes you think that `free(NULL)` throws an error? ANSI standard, section 4.10.3.2: "If ptr is a null pointer, no action occurs."
Christoph
"Fix the actual problem" - how do you think he's going to solve the problem, if you won't help him write a `free` replacement that highlights where the double-free occurs?
Steve Jessop
If you have two copies of the same pointer, then setting one to null via free() or a wrapper for free() will not prevent an attempt to free the second copy.
Jonathan Leffler
@onebyone: Writing a '`free` replacement highlights where double-free occurs' was not in the scope of the question - just one that doesn't cause problems. "Fixing the actual problem" is achieved by keeping meticulous track of where your pointers are allocated and deallocated and/ or having some programming rule/ heuristic about what parts of your code are responsible for each action.
Jason Musgrove
+7  A: 

Don't do that

An easy way is to define your wrapper function and #define free to call your function instead.

#undef free
#define free(x) wrapper_free(x)
/* ... */
int *data = malloc(42);
free(data); /* effectively calls wrapper_free(data) */

but ... don't do that!

pmg
you should do an `#undef free` before your defenition: the C standard allows stdlib functions to be aditionally implemented as macros
Christoph
Thank you. Fixed
pmg
+1  A: 

I agree with the other posts that say you shouldn't. Mainly, it'll be much more confusing for someone to look at your code and try to figure out what's going on (when they're used to see free only freeing memory).

If you're looking for a single line piece of code that will allow you to free and clear a pointer, I'd recommend using a macro, though there are other ways of doing this (creating a method that takes a pointer to your pointer).

#define FREEANDCLEAR(pointer)\
{\
   free(pointer);\
   pointer = 0;\
}

EDIT As Christoph mentioned in a comment, you can also make sure a user uses the macro like a function (ends the line with a semicolon by using a do while like this:

#define FREEANDCLEAR(pointer)\
do {\
   free(pointer);\
   pointer = 0;\
} while(0)

which will execute once and require an ending semicolon.

Mark Synowiec
there's no need to check `if(pointer)` - `free(NULL)` is valid and does nothing; also, you might want to wrap this in a `do..while()` loop to get function-like syntax
Christoph
thanks Christoph, I removed the if and added a second implementation using the do while!
Mark Synowiec
A: 

Look through the answers to the stackoverflow question, "Write your own memory manager".

Using the tools or techniques in those answers will let you incorporate a memory manager that checks for these types of problems so you can identify and fix them (as many other answers to your question indicate, it's not a good idea to use something like this as a workaround).

Michael Burr
+6  A: 

The following code intercepts calls to malloc(), realloc() and calloc() for logging.

When calling free(), it checks if the memory was previously allocated with one of these functions. If not, the program will be terminated. Freeing a null pointer will be reported, but execution will continue.

Header memdebug.h:

#undef free
#define free(PTR) memdebug_free(PTR, #PTR, __FILE__, __func__, __LINE__)

#undef malloc
#define malloc(SIZE) memdebug_log(malloc(SIZE))

#undef realloc
#define realloc(PTR, SIZE) memdebug_log(realloc(PTR, SIZE))

#undef calloc
#define calloc(COUNT, SIZE) memdebug_log(calloc(COUNT, SIZE))

#ifndef MEMDEBUG_H
#define MEMDEBUG_H

extern void memdebug_free(void *ptr, const char *ptr_arg, const char *file, 
    const char *func, int line);
extern void *memdebug_log(void *ptr);

#endif

Source memdebug.c:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

#ifndef MEMDEBUG_TABLE_SIZE
// log 16k allocations by default
#define MEMDEBUG_TABLE_SIZE 0x4000
#endif

static void *alloc_table[MEMDEBUG_TABLE_SIZE];
static size_t top;

void *memdebug_log(void *ptr)
{
    assert(top < sizeof alloc_table / sizeof *alloc_table);
    alloc_table[top++] = ptr;
    return ptr;
}

void memdebug_free(void *ptr, const char *ptr_arg, const char *file,
    const char *func, int line)
{
    if(!ptr)
    {
        fprintf(stderr,
            "%s() in %s, line %i: freeing null pointer `%s` -->continue\n",
            func, file, line, ptr_arg);

        return;
    }

    for(size_t i = top; i--; )
    {
        if(ptr == alloc_table[i])
        {
            free(ptr);
            --top;
            if(i != top) alloc_table[i] = alloc_table[top];
            return;
        }
    }

    fprintf(stderr,
        "%s() in %s, line %i: freeing invalid pointer `%s`-->exit\n",
        func, file, line, ptr_arg);

    exit(EXIT_FAILURE);
}
Christoph
+1, that should work in case of embedded environments or other cases when valgrind is not available.
Andrew Y
+1  A: 

Apart from all the other answers and references to memory management developement etc.

When double free occurs there are errors in the code that indicates that things are not done in the right order, etc.

When I started working with Java some 10 years ago, everybody praised Java since you did not have to bother about new/delete.

I was bothered (as I was used to develop in C/C++), since suddenly all the open/close, create/destroy, lock/unlock patterns that shows up in so many ways in software apart from malloc/free could be broken since everybody believed that all cleanup was automatic.

So when you have these problems, you probably have other problems as well sooner or later. Resources held up, DB connections being consumed, files locked on filesystems.

Double free is a signal that your program does not have a good structure, e.g things being allocated in one layer and freed in another.

Ernelli