Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic arrays: using realloc() without memory leaks

I use realloc to resize the memory allocated:

char **get_channel_name(void)   
{
    char **result;
    int n;

    result = (char **) 0;
    for (elem = snd_mixer_first_elem(handle), n = 0; elem; elem = snd_mixer_elem_next(elem)) {
        if (!snd_mixer_selem_is_active(elem))
            continue;
        if (snd_mixer_selem_has_playback_volume(elem) &&
            snd_mixer_selem_has_playback_switch(elem) &&
            snd_mixer_selem_has_capture_switch(elem)) {
            if (result == (char **) 0)
                result = (char **) malloc(sizeof(char *));
            else
                result = (char **) realloc(result, sizeof(char *) * (n + 1)); /* nulled but not freed upon failure */
            result[n++] = strdup(snd_mixer_selem_get_name(elem));
        }
    }

    if (result == (char **) 0)
        return NULL;

    result = (char **) realloc(result, sizeof(char *) * (n + 1)); /* nulled but not freed upon failure */
    result[n] = NULL;

    return result;
}

When I check code with cppcheck tool static C/C++ code analysis, printed the following warings:

Common realloc mistake: 'result' nulled but not freed upon failure

How can I fix these 2 possible memory leaks?

like image 566
Cecylia Avatar asked Dec 21 '14 13:12

Cecylia


People also ask

Can realloc () free the allocated memory space?

Description: The realloc() function allocates, reallocates, or frees the block of memory specified by old_blk based on the following rules: If old_blk is NULL, a new block of memory of size bytes is allocated. If the size is zero, the free() function is called to release the memory pointed to by old_blk.

Do you need to free memory after realloc?

Once you call realloc() , you do not have to free() the memory addressed by pointer passed to realloc() - you have to free() the memory addressed by the pointer realloc() returns. (Unless realloc() returns NULL , in which case the original block of memory - passed to realloc() - has to be free() 'd.)

Does realloc erase memory?

No, the data will be copied for you into the new block that the returned p points at, before the old block is freed. This all happens before realloc returns, so the new p points to your data still.

Does realloc initialize memory?

The realloc() function changes the size of the memory block pointed to by ptr to size bytes. The contents will be unchanged in the range from the start of the region up to the minimum of the old and new sizes. If the new size is larger than the old size, the added memory will not be initialized.


2 Answers

If realloc() fails it returns NULL.

So if you do (and assuming realloc() would fail)

result = realloc(result, ...);

result will be assigned NULL and what it pointed to is not free()ed and the address to be free()ed is lost.

To fix this do:

{
  void * tmp = realloc(result, ...);
  if (NULL == tmp)
  {
    /* Handle error case, propably freeing what result is pointing to. */
  }
  else
  {
    result = tmp;
  }
}
like image 108
alk Avatar answered Sep 22 '22 20:09

alk


The trick to fixing the "nulled but not freed upon failure" error is to store the value returned by realloc into a separate pointer, and check it for NULL before reassigning the old pointer:

char **tmp = (char **) realloc(result, sizeof(char *) * (n + 1));
if (tmp) {
    result = tmp;
} else {
    ... // Handle reallocation error
}

Now that the assignment of result is protected by NULL check, you have the old value to work with: you could free it if you want, or you could continue using it if you need to. The original code, on the other hand, does not give you the same option.

Note: When you pass NULL pointer to realloc, it behaves like malloc. That's why you can drop the conditional in the first use of realloc - replace this

if (result == (char **) 0)
    result = (char **) malloc(sizeof(char *));
else
    result = (char **) realloc(result, sizeof(char *) * (n + 1));

with this:

char** tmep = (char **) realloc(result, sizeof(char *) * (n + 1));
... // check temp and assign result here

Don't forget to set n to zero - currently, it's used uninitialized, which is undefined behavior.

like image 29
Sergey Kalinichenko Avatar answered Sep 21 '22 20:09

Sergey Kalinichenko