Search code examples
cmemory-managementvalgrindgeneric-programmingpointer-arithmetic

Valgrind error and generic list with one allocation per element


I'm experimenting with linked lists and tried to create a generic doubly linked list which allocates memory only once for each node. It means that the memory for the node and the element the node contains is allocated within same malloc invocation. The list itself is a structure which contains size of a unit (initialized with sizeof(struct which_is_an_element):

struct doubly_linked_list
{
    struct dll_node *head, *tail;
    size_t elem_size;
};

The node:

struct dll_node
{
    struct dll_node *next, *prev;
    void *elem;
};

Now, when I try to insert element and initialize it:

static struct dll_node * new_node(
        struct doubly_linked_list *l, 
        void *elem)
{
    struct dll_node *n = malloc (
        sizeof(struct dll_node) + l->elem_size);

    // This line throws valgrind error
    memcpy(n + sizeof(struct dll_node), elem, l->elem_size); 

    n->prev = n->next = NULL;
    n->elem = n + sizeof(struct dll_node);
    return n;
}

I get a valgrind error:

Invalid write of size 8
at 0x4C3217B: memcpy@@GLIBC_2.14 (vg_replace_strmem.c:1022) by 0x400705: new_node (doubly_linked_list.c:20)
by 0x400768: dl_list_insert_at_tail (doubly_linked_list.c:30)
by 0x40093F: main (main.c:24)
Address 0x5204280 is 480 bytes inside an unallocated block of size 4,194,112 in arena "client"

  1. Does really memcpy write inside unallocated block as the size of the allocated memory is equal to sizeof(struct which_is_an_element) + sizeof(dll_node)?
  2. Are those issues caused by valgrind thinking the allocated memory has size = sizeof(struct dll_node) because of the type of the pointer called n inside new_node function which points to the data?
  3. Is this approach good? Can I allocate the memory once for each element or should I allocate memory for the node and the element separately?
  4. If this approach is good then how could I get rid of this valgrind error?

Solution

  • Does really memcpy write inside unallocated block as the size of the allocated memory is equal to sizeof(struct which_is_an_element) + sizeof(dll_node)?

    Yes.

    Doing memcpy(n + sizeof(struct dll_node), ... points sizeof(struct dll_node) * sizeof *n (equal to sizeof(struct dll_node) * sizeof(struct dll_node)) bytes beyond where n points to.

    You probably want to just "seek" sizeof(struct dll_node) bytes.

    Do to so change your code to be:

    memcpy((char*)n + sizeof(struct dll_node), ...
    

    or, as n is a pointer to struct dll_node, just do

    memcpy(n + 1, ...
    

    to get beyond a struct dll_node.