Search code examples
cmemorymemory-managementmallocfree

Understanding use of memcpy on memory allocation


Looking at the source code for e2fsprogs and wanting to understand the use of internal memory routines. Allocating and freeing.

More to the point why use memcpy instead of direct handling?

Allocate

For example ext2fs_get_mem is:

/*
 *  Allocate memory.  The 'ptr' arg must point to a pointer.
 */
_INLINE_ errcode_t ext2fs_get_mem(unsigned long size, void *ptr)
{
    void *pp;

    pp = malloc(size);
    if (!pp)
        return EXT2_ET_NO_MEMORY;
    memcpy(ptr, &pp, sizeof (pp));
    return 0;
}

I guess the use of a local variable is as not to invalidate the passed ptr in case of malloc error.

  1. Why memcpy instead of setting ptr to pp on success?

Free

The memory is copied to a local variable, then freed, then memcpy on the passed pointer to pointer. As the allocation uses memcpy I guess it has to do some juggling on free as well.

  1. It can not free directly?
  2. And what does the last memcpy do? Isn't sizeof(p) size of int here?
/*
 * Free memory.  The 'ptr' arg must point to a pointer.
 */
_INLINE_ errcode_t ext2fs_free_mem(void *ptr)
{
    void *p;

    memcpy(&p, ptr, sizeof(p));
    free(p);
    p = 0;
    memcpy(ptr, &p, sizeof(p));
    return 0;
}

Example of use:

ext2_file_t is defined as:

typedef struct ext2_file *ext2_file_t;

where ext2_file has, amongst other members, char *buf.

In dump.c : dump_file()

Here we have:

    ext2_file_t e2_file;
    retval = ext2fs_file_open(current_fs, ino, 0, &e2_file);

It calls ext2fs_file_open() which do:


    ext2_file_t     file;
    retval = ext2fs_get_mem(sizeof(struct ext2_file), &file);
    retval = ext2fs_get_array(3, fs->blocksize, &file->buf);

And the free routine is for example:

    if (file->buf)
        ext2fs_free_mem(&file->buf);
    ext2fs_free_mem(&file);

Solution

  • You cannot assign directly to the ptr parameter, as this is a local variable. memcpying to ptr actually writes to where the pointer points to. Compare the following usage code:

    struct SomeData* data;
    //ext2fs_get_mem(256, data); // wrong!!!
    ext2fs_get_mem(256, &data);
    //                  ^ (!)
    

    You would achieve exactly the same with a double pointer indirection:

    _INLINE_ errcode_t ext2fs_get_mem_demo(unsigned long size, void** ptr)
    {
        *ptr = malloc(size);
        return *ptr ? 0 : EXT2_ET_NO_MEMORY;
    }
    

    but this variant requires the pointer being passed to to be of type void*, which is avoided by the original variant:

    void* p;
    ext2fs_get_mem_demo(256, &p);
    struct SomeData* data = p;
    

    Note: One additional variable and one additional line of code (or at very least one would need a cast)...

    Note, too, that in the usage example ext_file_t should be a typedef to a pointer type to make this work correctly (or uintptr_t) or at least have a pointer as its first member (address of struct and address of its first member are guaranteed to be the same in C).