Memory leak in PixbufLoader?

Hello, I’m seen memory leaks reported by valgrind and the asan memory sanitizer and I’m not really sure if I should be worried or if they are just false positives. The code gets the content of an animated gif from an external location (for test purposes it’s a file) and returns a PixbufAnimation.

gtk 3.24.20
gtkmm 3.24

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <gtkmm.h>

typedef Glib::RefPtr<Gdk::PixbufAnimation> AnimationPtr;

std::vector<unsigned char>
getExternalResource(const std::string& id) 
{
    std::vector<unsigned char> content;
    std::ifstream file(id, std::ios::binary | std::ios::ate);

    if (!file) 
        throw std::runtime_error("Resource not found.");

    std::streamsize size = file.tellg();

    file.seekg(0, std::ios::beg);
    content.resize(size);
    
    if (!file.read(static_cast<char*>(static_cast<void*>(content.data())), size))
        throw std::runtime_error("Could not read content.");

    return content;
}

AnimationPtr 
getAnimation(const std::string& id)
{
    const std::vector<unsigned char> data = getExternalResource(id);
    AnimationPtr animation;

    Glib::RefPtr<Gdk::PixbufLoader> loader = Gdk::PixbufLoader::create();

    loader->write(data.data(), data.size());
    loader->close();

    animation = loader->get_animation();

    return animation;
}

int main()
{
    auto app = Gtk::Application::create("org.gtkmm.example.base");
    Gtk::Window window;

    window.set_default_size(200, 200);

    AnimationPtr anim = getAnimation("image.gif");

    return app->run(window);
}

With asan:

g++ -O0 -g gtk-leak.cpp $(pkg-config gtkmm-3.0 --cflags --libs) -fsanitize=addres
ASAN_OPTIONS=new_delete_type_mismatch=0:detect_leaks=1:fast_unwind_on_malloc=0 G_SLICE=always-malloc G_DEBUG=gc-friendly ./a.out

Output:

=================================================================
==70718==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 88 byte(s) in 1 object(s) allocated from:
    #0 0x7feb76e38808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7feb75550e98 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)
    #2 0x7feb75569485 in g_slice_alloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70485)
    #3 0x7feb75569aad in g_slice_alloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70aad)
    #4 0x7feb7565d13f in g_type_create_instance (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x3b13f)
    #5 0x7feb7563c34c  (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1a34c)
    #6 0x7feb7563db44 in g_object_new_with_properties (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1bb44)
    #7 0x7feb7563e6f0 in g_object_new (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1c6f0)
    #8 0x7feb6b09e6fe  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x46fe)
    #9 0x7feb6b09ea83  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x4a83)
    #10 0x7feb6b09d600  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3600)
    #11 0x7feb6b09dbcf  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3bcf)
    #12 0x7feb75692280  (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10280)
    #13 0x7feb75692d59 in gdk_pixbuf_loader_write (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10d59)
    #14 0x7feb7671b0c3 in Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e0c3)
    #15 0x557e1a09ee1c in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:38
    #16 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #17 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #18 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Direct leak of 88 byte(s) in 1 object(s) allocated from:
    #0 0x7feb76e38808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7feb75550e98 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)
    #2 0x7feb75569485 in g_slice_alloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70485)
    #3 0x7feb75569aad in g_slice_alloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70aad)
    #4 0x7feb7565d13f in g_type_create_instance (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x3b13f)
    #5 0x7feb7563c34c  (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1a34c)
    #6 0x7feb7563db44 in g_object_new_with_properties (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1bb44)
    #7 0x7feb7563e6f0 in g_object_new (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1c6f0)
    #8 0x7feb6b09e6fe  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x46fe)
    #9 0x7feb6b09ea83  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x4a83)
    #10 0x7feb6b09d022  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3022)
    #11 0x7feb6b09dbcf  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3bcf)
    #12 0x7feb75692c8b in gdk_pixbuf_loader_write (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10c8b)
    #13 0x7feb7671b0c3 in Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e0c3)
    #14 0x557e1a09ee1c in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:38
    #15 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #16 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #17 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Direct leak of 88 byte(s) in 1 object(s) allocated from:
    #0 0x7feb76e38808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7feb75550e98 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)
    #2 0x7feb75569485 in g_slice_alloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70485)
    #3 0x7feb75569aad in g_slice_alloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70aad)
    #4 0x7feb7565d13f in g_type_create_instance (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x3b13f)
    #5 0x7feb7563c34c  (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1a34c)
    #6 0x7feb7563db44 in g_object_new_with_properties (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1bb44)
    #7 0x7feb7563e6f0 in g_object_new (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1c6f0)
    #8 0x7feb6b09e6fe  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x46fe)
    #9 0x7feb6b09ea83  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x4a83)
    #10 0x7feb6b09d0de  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x30de)
    #11 0x7feb6b09dbcf  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3bcf)
    #12 0x7feb75692280  (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10280)
    #13 0x7feb75692d59 in gdk_pixbuf_loader_write (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10d59)
    #14 0x7feb7671b0c3 in Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e0c3)
    #15 0x557e1a09ee1c in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:38
    #16 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #17 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #18 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Indirect leak of 1327104 byte(s) in 44 object(s) allocated from:
    #0 0x7feb76e38c3e in __interceptor_realloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:163
    #1 0x7feb75550f3f in g_realloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57f3f)
    #2 0x7feb7551978a  (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x2078a)
    #3 0x7feb75519b99 in g_array_append_vals (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x20b99)
    #4 0x7feb7551b35d in g_byte_array_append (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x2235d)
    #5 0x7feb6b09d06e  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x306e)
    #6 0x7feb6b09dbcf  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3bcf)
    #7 0x7feb75692c8b in gdk_pixbuf_loader_write (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10c8b)
    #8 0x7feb7671b0c3 in Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e0c3)
    #9 0x557e1a09ee1c in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:38
    #10 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #11 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #12 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Indirect leak of 640000 byte(s) in 1 object(s) allocated from:
    #0 0x7feb76e38808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7feb7568a8a6 in gdk_pixbuf_new (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x88a6)
    #2 0x7feb6b09e9fb  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x49fb)
    #3 0x7feb6b09ea8b  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x4a8b)
    #4 0x7feb6b09d600  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3600)
    #5 0x7feb6b09dbcf  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3bcf)
    #6 0x7feb75692280  (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10280)
    #7 0x7feb75692d59 in gdk_pixbuf_loader_write (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10d59)
    #8 0x7feb7671b0c3 in Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e0c3)
    #9 0x557e1a09ee1c in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:38
    #10 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #11 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #12 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Indirect leak of 2408 byte(s) in 43 object(s) allocated from:
    #0 0x7feb76e38a06 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:153
    #1 0x7feb75550ef0 in g_malloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57ef0)
    #2 0x7feb6b09d45f  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x345f)
    #3 0x7feb6b09dbcf  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3bcf)
    #4 0x7feb75692c8b in gdk_pixbuf_loader_write (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10c8b)
    #5 0x7feb7671b0c3 in Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e0c3)
    #6 0x557e1a09ee1c in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:38
    #7 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #8 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #9 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Indirect leak of 1720 byte(s) in 43 object(s) allocated from:
    #0 0x7feb76e38808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7feb75550e98 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)
    #2 0x7feb75569485 in g_slice_alloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70485)
    #3 0x7feb75519913 in g_array_sized_new (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x20913)
    #4 0x7feb6b09d46e  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x346e)
    #5 0x7feb6b09dbcf  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3bcf)
    #6 0x7feb75692c8b in gdk_pixbuf_loader_write (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10c8b)
    #7 0x7feb7671b0c3 in Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e0c3)
    #8 0x557e1a09ee1c in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:38
    #9 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #10 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #11 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Indirect leak of 1032 byte(s) in 43 object(s) allocated from:
    #0 0x7feb76e38808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7feb75550e98 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)
    #2 0x7feb75569485 in g_slice_alloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70485)
    #3 0x7feb75546bc7 in g_list_append (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x4dbc7)
    #4 0x7feb6b09d5b7  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x35b7)
    #5 0x7feb6b09dbcf  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3bcf)
    #6 0x7feb75692c8b in gdk_pixbuf_loader_write (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10c8b)
    #7 0x7feb7671b0c3 in Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e0c3)
    #8 0x557e1a09ee1c in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:38
    #9 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #10 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #11 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Indirect leak of 848 byte(s) in 1 object(s) allocated from:
    #0 0x7feb76e38808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7feb75550e98 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)
    #2 0x7feb75569485 in g_slice_alloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70485)
    #3 0x7feb75569aad in g_slice_alloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70aad)
    #4 0x7feb7565d13f in g_type_create_instance (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x3b13f)
    #5 0x7feb7563c34c  (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1a34c)
    #6 0x7feb7563db44 in g_object_new_with_properties (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1bb44)
    #7 0x7feb7563e6f0 in g_object_new (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1c6f0)
    #8 0x7feb6b09c8d6  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x28d6)
    #9 0x7feb6b09cbd5  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x2bd5)
    #10 0x7feb7569220b  (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x1020b)
    #11 0x7feb75692d59 in gdk_pixbuf_loader_write (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10d59)
    #12 0x7feb7671b0c3 in Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e0c3)
    #13 0x557e1a09ee1c in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:38
    #14 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #15 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #16 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Indirect leak of 96 byte(s) in 1 object(s) allocated from:
    #0 0x7feb76e38808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7feb75550e98 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)
    #2 0x7feb75569485 in g_slice_alloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70485)
    #3 0x7feb75569aad in g_slice_alloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70aad)
    #4 0x7feb7565d13f in g_type_create_instance (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x3b13f)
    #5 0x7feb7563c34c  (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1a34c)
    #6 0x7feb7563e377 in g_object_new_valist (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1c377)
    #7 0x7feb7563e6cc in g_object_new (/lib/x86_64-linux-gnu/libgobject-2.0.so.0+0x1c6cc)
    #8 0x7feb7568d427 in gdk_pixbuf_new_from_data (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0xb427)
    #9 0x7feb7568a8d1 in gdk_pixbuf_new (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x88d1)
    #10 0x7feb6b09e9fb  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x49fb)
    #11 0x7feb6b09ea8b  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x4a8b)
    #12 0x7feb6b09d600  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3600)
    #13 0x7feb6b09dbcf  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3bcf)
    #14 0x7feb75692280  (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10280)
    #15 0x7feb75692d59 in gdk_pixbuf_loader_write (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10d59)
    #16 0x7feb7671b0c3 in Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e0c3)
    #17 0x557e1a09ee1c in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:38
    #18 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #19 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #20 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Indirect leak of 56 byte(s) in 1 object(s) allocated from:
    #0 0x7feb76e38a06 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:153
    #1 0x7feb75550ef0 in g_malloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57ef0)
    #2 0x7feb6b09d45f  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x345f)
    #3 0x7feb6b09dbcf  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3bcf)
    #4 0x7feb75692280  (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10280)
    #5 0x7feb75692d59 in gdk_pixbuf_loader_write (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10d59)
    #6 0x7feb7671b0c3 in Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e0c3)
    #7 0x557e1a09ee1c in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:38
    #8 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #9 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #10 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Indirect leak of 48 byte(s) in 1 object(s) allocated from:
    #0 0x7feb76e3a587 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cc:104
    #1 0x7feb76719517 in Gdk::PixbufAnimation_Class::wrap_new(_GObject*) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3c517)
    #2 0x7feb764a49d1 in Glib::wrap_auto(_GObject*, bool) (/lib/x86_64-linux-gnu/libglibmm-2.4.so.1+0x6e9d1)
    #3 0x7feb76719263 in Glib::wrap(_GdkPixbufAnimation*, bool) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3c263)
    #4 0x7feb7671b20b in Gdk::PixbufLoader::get_animation() (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e20b)
    #5 0x557e1a09ee75 in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:41
    #6 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #7 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #8 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Indirect leak of 40 byte(s) in 1 object(s) allocated from:
    #0 0x7feb76e38808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7feb75550e98 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)
    #2 0x7feb75569485 in g_slice_alloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70485)
    #3 0x7feb75519913 in g_array_sized_new (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x20913)
    #4 0x7feb6b09d46e  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x346e)
    #5 0x7feb6b09dbcf  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3bcf)
    #6 0x7feb75692280  (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10280)
    #7 0x7feb75692d59 in gdk_pixbuf_loader_write (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10d59)
    #8 0x7feb7671b0c3 in Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e0c3)
    #9 0x557e1a09ee1c in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:38
    #10 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #11 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #12 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Indirect leak of 32 byte(s) in 1 object(s) allocated from:
    #0 0x7feb76e38808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7feb75550e98 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)
    #2 0x7feb7552b829 in g_datalist_id_set_data_full (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x32829)
    #3 0x7feb767194c3 in Gdk::PixbufAnimation::PixbufAnimation(_GdkPixbufAnimation*) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3c4c3)
    #4 0x7feb76719525 in Gdk::PixbufAnimation_Class::wrap_new(_GObject*) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3c525)
    #5 0x7feb764a49d1 in Glib::wrap_auto(_GObject*, bool) (/lib/x86_64-linux-gnu/libglibmm-2.4.so.1+0x6e9d1)
    #6 0x7feb76719263 in Glib::wrap(_GdkPixbufAnimation*, bool) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3c263)
    #7 0x7feb7671b20b in Gdk::PixbufLoader::get_animation() (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e20b)
    #8 0x557e1a09ee75 in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:41
    #9 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #10 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #11 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

Indirect leak of 24 byte(s) in 1 object(s) allocated from:
    #0 0x7feb76e38808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x7feb75550e98 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)
    #2 0x7feb75569485 in g_slice_alloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x70485)
    #3 0x7feb75546bc7 in g_list_append (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x4dbc7)
    #4 0x7feb6b09d5b7  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x35b7)
    #5 0x7feb6b09dbcf  (/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so+0x3bcf)
    #6 0x7feb75692280  (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10280)
    #7 0x7feb75692d59 in gdk_pixbuf_loader_write (/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0+0x10d59)
    #8 0x7feb7671b0c3 in Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1+0x3e0c3)
    #9 0x557e1a09ee1c in getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/user/Code/misc/gtk-leak.cpp:38
    #10 0x557e1a09f203 in main /home/user/Code/misc/gtk-leak.cpp:53
    #11 0x7feb74fba082 in __libc_start_main ../csu/libc-start.c:308
    #12 0x557e1a09e76d in _start (/home/user/Code/misc/a.out+0x376d)

SUMMARY: AddressSanitizer: 1973672 byte(s) leaked in 184 allocation(s).

With valgrind the situation is similar:

g++ -O0 -g gtk-leak.cpp $(pkg-config gtkmm-3.0 --cflags --libs)
G_SLICE=always-malloc G_DEBUG=gc-friendly valgrind --leak-check=full --suppressions=/usr/share/gtk-3.0/valgrind/gtk.supp --suppressions=/usr/share/glib-2.0/valgrind/glib.supp ./a.out 

==76657== Memcheck, a memory error detector
==76657== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==76657== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==76657== Command: ./a.out
==76657== 
--76657-- WARNING: unhandled amd64-linux syscall: 315
--76657-- You may be able to write your own handler.
--76657-- Read the file README_MISSING_SYSCALL_OR_IOCTL.
--76657-- Nevertheless we consider this a bug.  Please report
--76657-- it at http://valgrind.org/support/bug_reports.html.
==76657== 
==76657== HEAP SUMMARY:
==76657==     in use at exit: 5,356,200 bytes in 27,026 blocks
==76657==   total heap usage: 669,791 allocs, 642,765 frees, 42,470,755 bytes allocated
[... redated to fit the limit...]
==76657== 16,384 bytes in 1 blocks are definitely lost in loss record 11,518 of 11,537
==76657==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==76657==    by 0x5EEAE98: g_malloc (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.6)
==76657==    by 0x5EF57D3: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.6)
==76657==    by 0x4011B99: call_init.part.0 (dl-init.c:72)
==76657==    by 0x4011CA0: call_init (dl-init.c:30)
==76657==    by 0x4011CA0: _dl_init (dl-init.c:119)
==76657==    by 0x4001139: ??? (in /usr/lib/x86_64-linux-gnu/ld-2.31.so)
==76657== 
==76657== 32,768 bytes in 1 blocks are possibly lost in loss record 11,528 of 11,537
==76657==    at 0x483DFAF: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==76657==    by 0x5EEAF3F: g_realloc (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.6)
==76657==    by 0x5EB378A: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.6)
==76657==    by 0x5EB3B99: g_array_append_vals (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.6)
==76657==    by 0x5EB535D: g_byte_array_append (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.6)
==76657==    by 0x484D06E: ??? (in /usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so)
==76657==    by 0x484DBCF: ??? (in /usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so)
==76657==    by 0x611BC8B: gdk_pixbuf_loader_write (in /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0.4000.0)
==76657==    by 0x4E410C3: Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1.1.0)
==76657==    by 0x10A8A0: getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (gtk-leak.cpp:38)
==76657==    by 0x10AA32: main (gtk-leak.cpp:53)
==76657== 
==76657== 1,940,640 (56 direct, 1,940,584 indirect) bytes in 1 blocks are definitely lost in loss record 11,537 of 11,537
==76657==    at 0x483DD99: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==76657==    by 0x5EEAEF0: g_malloc0 (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.6)
==76657==    by 0x484D45F: ??? (in /usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so)
==76657==    by 0x484DBCF: ??? (in /usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-gif.so)
==76657==    by 0x611BC8B: gdk_pixbuf_loader_write (in /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0.4000.0)
==76657==    by 0x4E410C3: Gdk::PixbufLoader::write(unsigned char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libgdkmm-3.0.so.1.1.0)
==76657==    by 0x10A8A0: getAnimation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (gtk-leak.cpp:38)
==76657==    by 0x10AA32: main (gtk-leak.cpp:53)
==76657== 
==76657== LEAK SUMMARY:
==76657==    definitely lost: 16,704 bytes in 5 blocks
==76657==    indirectly lost: 1,940,584 bytes in 179 blocks
==76657==      possibly lost: 38,192 bytes in 82 blocks
==76657==    still reachable: 2,924,706 bytes in 20,514 blocks
==76657==                       of which reachable via heuristic:
==76657==                         length64           : 36,520 bytes in 481 blocks
==76657==                         newarray           : 2,448 bytes in 73 blocks
==76657==         suppressed: 326,438 bytes in 5,368 blocks
==76657== Reachable blocks (those to which a pointer was found) are not shown.
==76657== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==76657== 
==76657== For lists of detected and suppressed errors, rerun with: -s
==76657== ERROR SUMMARY: 53 errors from 53 contexts (suppressed: 20 from 20)

Is there something that I’m forgeting when dealing with animations? What should I do to be sure that there isn’t a real leak?

Thanks

It would be good to have debugging symbols for gdk-pixbuf and glib/gobject available, so that’s easier to see the complete call stack.

Good idea, just to be sure the env variables that should be used for this are: G_SLICE=always-malloc G_DEBUG=gc-friendly right?

This is the asan output: https://termbin.com/hggh
This is the valgrind output: https://termbin.com/htbs1

G_SLICE=always-malloc is unnecessary: GLib will automatically bypass the slice allocator using the system one when it detects it’s running under Valgrind.

Looking at the source, it doesn’t seem to be a leak in gdk-pixbuf or the C++ bindings.

You need to release the memory of the Gdk::PixbufAnimation pointer manually, otherwise you’re leaving it to the OS to collect the memory, and Valgrind will accurately warn you that you didn’t do that.

But it’s a Glib::RefPtr shouldn’t it get released when it goes out of scope?

Sorry, I’m not a C++ developer, so I can’t answer that.

According to Valgrind, the Animation instance isn’t being released, so you’re “leaking” all the data attached to it.

Actually I think the issue may be in GdkPixbufLoader and not in the C++ binding at all.

I checked the gdk-pixbuf code before answering, and the objects are being correctly freed by the loader.

Of course, an easier way to check is to re-write the example in C, and running it under Valgrind.

I just did that, please check if the code is ok:

#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>

static GdkPixbufAnimation* 
loadAnimation()
{
    GdkPixbufLoader* loader = gdk_pixbuf_loader_new();
    GdkPixbufAnimation* result;
    FILE* file = fopen("image.gif", "rb");
    guchar data[512];
    gsize size = 0;

    while((size = fread(data, sizeof(guchar), 512, file)) > 0)
    { 
        gdk_pixbuf_loader_write(loader, data, size, NULL);
    }

    gdk_pixbuf_loader_close(loader, NULL);

    result = gdk_pixbuf_loader_get_animation(loader);
    g_object_unref(loader);

    return result;
}

static void
activate(GtkApplication* app, gpointer uder_data)
{
    GtkWidget* window;

    window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Window");
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);
    gtk_widget_show_all(window);
}


int
main(int argc, char** argv)
{
    GtkApplication* app;
    int status;
    GdkPixbufAnimation* gif;

    app = gtk_application_new("org.gtk.example", G_APPLICATION_FLAGS_NONE);
    g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
    
    gif = loadAnimation();

    status = g_application_run(G_APPLICATION(app), argc, argv);

    g_object_unref(gif);
    g_object_unref(app);

    return status;
}

compiled with: gcc gtk3-leak.c -g -O0 $(pkg-config gtk+-3.0 --cflags --libs)

output: https://termbin.com/xtog
and the code (just in case): https://termbin.com/zyjm

It seems that the GdkPixbufAnimation is not released properly?

Which version of gdk-pixbuf is this? The Valgrind trace does not match the current source code.

Edited to add commit dd3aa9ed64a0a370c8150f98e849ffc6e73da827 seems to be relevant:

commit dd3aa9ed64a0a370c8150f98e849ffc6e73da827
Author: Sebastian Keller <skeller@gnome.org>
Date:   Mon Jan 27 02:59:35 2020 +0100

    GIF: Plug animation data and iterator leak
    
    The get_static_image() method for gifs is leaking an iterator and since
    the iterator has a reference to the animation, this results in all
    animation frames being leaked.

You will need gdk-pixbuf ≥ 2.42.0.

This is why I couldn’t find the leak when looking in the sources. :slight_smile:

Oh noes, I have version: 2.40.0+dfsg-3ubuntu0.4 (Ubuntu 20.04.5 LTS)

Thanks a lot!

You can ask Ubuntu to cherry pick the fix in their LTS package.

1 Like