简体   繁体   中英

Why am I getting memory leaks ? Is there something missing in the following C code?

I am trying to write a custom lib in c for a personal project. The lib includes some common data structures. I am allocating the list on the heap and I think I am cleaning up properly, but that is not the case. On running leaks --atExit --list --./main (mac m1), I am getting a total of 4 leaks in the following code.

leaks Report Version: 3.0
Process 18747: 221 nodes malloced for 17 KB
Process 18747: 4 leaks for 96 total leaked bytes. 

Stack trace is as follows:

Leak: 0x14e7040f0  size=16  zone: MallocStackLoggingLiteZone_0x102a40000   malloc in main  C  main
    Call stack: 0x180c6be50 (dyld) start | 0x1028475a8 (main) main  main.c:38 | 0x180dffd00 (libsystem_malloc.dylib) _malloc_zone_malloc_instrumented_or_legacy

Leak: 0x14e704100  size=16  zone: MallocStackLoggingLiteZone_0x102a40000   malloc in main  C  main
    Call stack: 0x180c6be50 (dyld) start | 0x1028475c4 (main) main  main.c:41 | 0x180dffd00 (libsystem_malloc.dylib) _malloc_zone_malloc_instrumented_or_legacy

Leak: 0x14e704180  size=32  zone: MallocStackLoggingLiteZone_0x102a40000   malloc in node_init  C  main
    Call stack: 0x180c6be50 (dyld) start | 0x10284761c (main) main  main.c:48 | 0x102847a30 (main) owl_sll_binsert  singly_linked_list.c:88 | 0x102847ab8 (main) node_init  singly_linked_list.c:60 | 0x180dffd00 (libsystem_malloc.dylib) _malloc_zone_malloc_instrumented_or_legacy

Leak: 0x14e7041a0  size=32  zone: MallocStackLoggingLiteZone_0x102a40000   malloc in node_init  C  main
    Call stack: 0x180c6be50 (dyld) start | 0x10284761c (main) main  main.c:48 | 0x102847a30 (main) owl_sll_binsert  singly_linked_list.c:88 | 0x102847ac4 (main) node_init  singly_linked_list.c:61 | 0x180dffd00 (libsystem_malloc.dylib) _malloc_zone_malloc_instrumented_or_legacy

singly_linked_list.c

#include "singly_linked_list.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>

/* Opaque type */

typedef struct SinglyLinkedList
{
    owl_sll_node_t *head;
    owl_sll_node_t *tail;
    u_long length;
    size_t size;
    void (*elfree)(void *item);
} owl_sll_t;

//

owl_sll_t *owl_sll_init(size_t size, void (*elfree)(void *item))
{
    owl_sll_t *list = malloc(sizeof(owl_sll_t));
    list->head = NULL;
    list->tail = NULL;
    list->length = 0;
    list->size = size;
    list->elfree = elfree;
    return list;
}

void owl_sll_free(owl_sll_t *list)
{
    if (!list->length)
    {
        free(list);
        return;
    }

    owl_sll_node_t *cursor = list->head;

    while (cursor)
    {
        if (list->elfree)
            list->elfree(cursor->data);
        else
            free(cursor->data);

        free(cursor);
        cursor = cursor->next;
    }

    free(list);
}

static owl_sll_node_t *node_init(void *data, size_t size)
{
    static unsigned int id = 0;
    owl_sll_node_t *node = malloc(sizeof(owl_sll_node_t));
    node->data = malloc(size);

    // copy the content of the data
    for (int i = 0; i < size; i++)
        *(char *)(node->data + i) = *(char *)(data + i);

    node->next = NULL;
    node->id = id++;
    return node;
}

static void node_free(owl_sll_t *list, owl_sll_node_t *node)
{
    if (!node) return;
    // break any link if exist
    node->next = NULL;

    if (list->elfree)
        list->elfree(node->data);
    else
        free(node->data);

    free(node);
}

void owl_sll_binsert(owl_sll_t *list, void *data)
{
    owl_sll_node_t *node = node_init(data, list->size);
    if (!list->head)
    {
        list->head = node;
        list->tail = node;
    }
    else
    {
        list->tail->next = node;
        list->tail = node;
    }
    list->length++;
}

void *owl_sll_bremove(owl_sll_t *list)
{
    if (!list->length) return NULL;
    owl_sll_node_t *cursor = list->head;

    while (cursor->next && cursor->next->next)
    {
        cursor = cursor->next;
    }

    void *data = cursor->data;

    node_free(list, cursor->next);
    list->tail = cursor;
    list->tail->next = NULL;
    list->length--;

    if (list->length == 0)
    {
        list->head = NULL;
        list->tail = NULL;
    }

    return data;
}

void owl_sll_finsert(owl_sll_t *list, void *data)
{
    owl_sll_node_t *node = node_init(data, list->size);
    if (!list->head)
    {
        list->head = node;
        list->tail = node;
    }
    else
    {
        node->next = list->head;
        list->head = node;
    }
    list->length++;
}

void *owl_sll_fremove(owl_sll_t *list)
{
    if (!list->length) return NULL;

    void *data = list->head->data;
    owl_sll_node_t *node_to_free = list->head;

    list->head = list->head->next;
    node_free(list, node_to_free);
    list->length--;

    if (list->length == 0)
    {
        list->head = NULL;
        list->tail = NULL;
    }

    return data;
}

void owl_sll_print(owl_sll_t *list, void (*format)(void *data), char *connection_sym)
{
    if (!list->length)
    {
        printf("EMPTY\n");
        return;
    }

    owl_sll_node_t *cursor = list->head;
    char *conn_sym = connection_sym ? connection_sym : "->";

    while (cursor)
    {
        format(cursor->data);
        printf(" %s ", conn_sym);
        cursor = cursor->next;
    }

    printf("END\n");
}

// Getters

owl_sll_node_t *owl_sll_head(owl_sll_t *list)
{
    return list->head;
}

owl_sll_node_t *owl_sll_tail(owl_sll_t *list)
{
    return list->tail;
}

unsigned long int owl_sll_length(owl_sll_t *list)
{
    return list->length;
}

singly_linked_list.h

#ifndef linked_list_h
#define linked_list_h

#include <stdlib.h>

typedef struct Node
{
    unsigned int id;
    void *data;
    struct Node *next;
} owl_sll_node_t;

typedef struct SinglyLinkedList owl_sll_t;

owl_sll_t *owl_sll_init(size_t size, void (*elfree)(void *item));
void owl_sll_free(owl_sll_t *list);
void owl_sll_binsert(owl_sll_t *list, void *data);
void *owl_sll_bremove(owl_sll_t *list);
void owl_sll_finsert(owl_sll_t *list, void *data);
void *owl_sll_fremove(owl_sll_t *list);
void owl_sll_print(owl_sll_t *list, void (*format)(void *data), char *connector_symbol);

// Getters

owl_sll_node_t *owl_sll_head(owl_sll_t *list);
owl_sll_node_t *owl_sll_tail(owl_sll_t *list);
unsigned long int owl_sll_length(owl_sll_t *list);

#endif /* linked_list_h */

main.c

#include "data_structure/singly_linked_list.h"
#include <stdio.h>

typedef struct a
{
    int id;
} a_t;

typedef struct b
{
    int id;
    a_t *a;
} b_t;

void elfree(void *item)
{
    b_t *b = item;
    free(b->a);
    free(b);
}

int main(int argc, const char *argv[])
{
    printf("OWL 🦉\n");
    int a = 1, b = 2;
    a_t *a1 = malloc(sizeof(a_t));
    a1->id = 1;

    a_t *a2 = malloc(sizeof(a_t));
    a2->id = 2;

    owl_sll_t *list = owl_sll_init(sizeof(b_t), elfree);
    owl_sll_binsert(list, &(b_t){.id = 1, .a=a1 });
    owl_sll_binsert(list, &(b_t){.id = 2, .a=a2 });

    owl_sll_free(list);
}

  1. In main() you need to free a1 and a2 .

  2. In owl_sll_free() you have a use after free undefined behavior:

        free(cursor);
        cursor = cursor->next;

instead you want to a tmp pointer to cursor, advance cursor, then free what tmp pointers to:

void owl_sll_free(owl_sll_t *list) {
    for(owl_sll_node_t *cursor = list->head; cursor;) {
        owl_sll_node_t *tmp = cursor;
        cursor = cursor->next;
        if (list->elfree)
            list->elfree(tmp->data);
        else
            free(tmp->data);
        free(tmp);
    }
    free(list);
}

I don't have the leaks program but valgrind is happy after those two changes:

==553760== HEAP SUMMARY:
==553760==     in use at exit: 0 bytes in 0 blocks
==553760==   total heap usage: 8 allocs, 8 frees, 1,152 bytes allocated
==553760== 
==553760== All heap blocks were freed -- no leaks are possible

Bonus suggestion:

In owl_sll_init() if you fallback to free:

    list->elfree = elfree ? elfree : free;

Then you can eliminate the conditional in own_sll_free():

void owl_sll_free(owl_sll_t *list) {
    for(owl_sll_node_t *cursor = list->head; cursor;) {
        owl_sll_node_t *tmp = cursor;
        cursor = cursor->next;
        list->elfree(tmp->data);
        free(tmp);
    }
    free(list);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM