Search code examples
cmemory-leaksglib

Why does this function cause memory leaks?


I'm playing around with libfprint and I created a really simple function:

#include <libfprint-2/fprint.h>
#include <glib-2.0/glib-unix.h>

void start_device() {
    FpContext *ctx = fp_context_new();
    fp_context_enumerate(ctx);
}

int main() {
    start_device();
}

I'm compiling with gcc using gcc -fsanitize=address and I'm getting memory leaks and I can't figure out why.

Edit: Output

=================================================================
==55025==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 3 byte(s) in 1 object(s) allocated from:
    #0 0x7f14a8aba6af in __interceptor_malloc (/lib64/libasan.so.8+0xba6af)
    #1 0x7f14a56d3017 in __vasprintf_internal (/lib64/libc.so.6+0x80017)
    #2 0x7af10c9e9cbbccff  (<unknown module>)

SUMMARY: AddressSanitizer: 3 byte(s) leaked in 1 allocation(s).

Edit 2: If I delete fp_context_enumerate(ctx); I don't get memory leaks, so I guess there's the problem but I don't know why.


Solution

  • That function is tagged with the transfer full label, meaning it's transferring full responsibility to the caller for the pointer. See here for more detail.

    That means you're responsible for freeing it when you're done. And, by freeing, I don't mean the normal C free function. The returned pointer is a GObject one, so you should simply g_object_unref it and let the object reference counting framework take care of the actual freeing.

    In other words, something like:

    include <libfprint-2/fprint.h>
    #include <glib-2.0/glib-unix.h>
    
    FpContext *start_device() {
        FpContext *ctx = fp_context_new();
        fp_context_enumerate(ctx);
        return ctx;
    }
    
    void stop_device(FpContext *ctx) {
        g_object_unref(ctx);
    }
    
    int main() {
        FpContext *ctx = start_device();
    
        // Work your magic.
    
        stop_device(ctx);
    }
    

    But keep in mind that un-freed memory and memory leaks aren't quite necessarily the same thing (or even a bad thing), it's just when they add up over time that they cause issues.

    For example, startup code may leak memory that it never cleans up, such as if a 4K buffer is created once for printf purposes the first time it's called. It may well rely on process exit to "free" that memory.

    Obviously, this allocation won't be a real problem because it's not allocating 4K continuously, just once at the start. In that sense, it's no more serious than having a stack allocated for your process :-)

    In fact, based on your edit:

    If I delete fp_context_enumerate(ctx);, I don't get memory leaks so I guess there's the problem but I don't know why.

    That's exactly what I was stating above (confirmed by the source code). Enumerating with fp_context_enumerate() will, the first time it's called for a context, allocate some memory to detect and store details on each fingerprint scanning device, and that memory is subsequently kept around.

    Then a call to fp_context_get_devices() will first call fp_enumerate() (which returns immediately if already done for that context), then return a pointer to internal data set up during enumeration (1).


    (1) That's why it's a transfer none operation, it just gives you a pointer to internal data. Because that data is never actually freed within the process, there's no reference counting needed for it.