GNOME Bugzilla – Bug 787626
pixels never get freed after gdk_pixbuf_get_pixels_with_length() call
Last modified: 2018-04-25 14:56:00 UTC
Created attachment 359715 [details] Reproducer. Be carefull memory leaks very fast Steps to reproduce: 1. Create a GBytes with g_bytes_new_static () (say, 3MB size) 2. Create a GdkPixbuf with gdk_pixbuf_new_from_bytes() 3. Call gdk_pixbuf_get_pixels_with_length() 4. Destroy GdkPixbuf 5. Go to step 2 If you run the algorithm endlessly you will see that RAM gets monotonously consumed. I've attached a C reproducer and some valgrind output. You can see that memory allocated in g_bytes_unref_to_data() is never freed. ==12342== 688,914,432 bytes in 219 blocks are definitely lost in loss record 312 of 312 ==12342== at 0x4C2BEDF: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==12342== by 0x5300C78: g_malloc (gmem.c:94) ==12342== by 0x531B357: g_memdup (gstrfuncs.c:391) ==12342== by 0x52D6842: g_bytes_unref_to_data (gbytes.c:466) ==12342== by 0x4E3F7D0: gdk_pixbuf_get_pixels_with_length (gdk-pixbuf.c:716) ==12342== by 0x4008E1: main (in /home/vkirsano/sandbit/gdk-pixbuf/a.out) I guess to fix this we can use the fact that the memory (duplicated or not) which returned by g_bytes_unref_to_data() can always be freed by g_free(). Although I'm not sure if it's documented anywhere. So.. since we set mut_pixbuf->pixels and mut_pixbuf->bytes inside gdk_pixbuf_get_pixels_with_length() we also have to set mut_pixbuf->destroy_fn to something. And since we know (at least it can be seen in the implementation of g_bytes_unref_to_data()) that the new memory can be freed by g_free() it can be a safe destructor candidate. I've also attached a sketch of a patch fixing this.
Created attachment 359716 [details] Valgrind output
Created attachment 359717 [details] [review] The patch
Review of attachment 359717 [details] [review]: Please, don't change random fields in Bugzilla. ::: gdk-pixbuf/gdk-pixbuf.c @@ +716,3 @@ mut_pixbuf->pixels = g_bytes_unref_to_data (pixbuf->bytes, &len); mut_pixbuf->bytes = NULL; + mut_pixbuf->destroy_fn = free_buffer; This is technically correct, but I'm a bit miffed as to why GBytes is copying the data if the bytes buffer is static.
The semantics of g_bytes_unref_to_data() are definitely too clever for its own good…
Thanks for your patience, and the patch.