Memory leak of g_object_unref in libglib-2.0.so.0.7200.2

Why is it that the same environment, the same test method, when I link my program to libglib-2.0.so.0.7200.2 and libgobject-2.0.so.0.7200.2 there is a memory leak of g_object_unref, while in libglib-2.0.so.0.6800.4 and libgobject-2.0.so.0.6800.4 versions do not have this issue, How do I debug this?

Here is the leak stack information:
Leak of 375328 bytes in 11729 objects allocated from:
@ ffffb38d8420 g_malloc
@ ffffb38b1d58 g_data_set_internal
@ ffffb380aa40 g_object_notify_queue_freeze
@ ffffb380bc20 g_object_unref
@ 41cc4c jmnd_free_entry
@ 41bfd0 jmnd_qdict_del
@ 47c430 jmnd_ctrlq_del_ctrlq
@ 47c628 jmnd_ctrlq_modify_local_data_post
@ 47e858 jmnd_ctrlq_cmd_proc
@ 43ea84 jmnd_notify_net_ctrlq_del
@ 43d244 jmnd_virtio_net_finalize
@ ffffb380bd3c g_object_unref
@ 41cc4c jmnd_free_entry
@ 41bfd0 jmnd_qdict_del
@ 477ea4 jmnd_vm_dev_clean_all
@ 4711d0 jmnd_vm_delete
@ 422700 jmnd_xml_cb_vm_destroy

Here is the ldd information for the program:
[root@bclinux-corsica lib64]# ldd /usr/bin/hyper_commander
linux-vdso.so.1 (0x0000007f9485e000)
libglib-2.0.so.0 => /usr/lib64/libglib-2.0.so.0 (0x0000007f946c0000)
libgobject-2.0.so.0 => /usr/lib64/libgobject-2.0.so.0 (0x0000007f94640000)
libxml2.so.2 => /usr/lib64/libxml2.so.2 (0x0000007f944b0000)
libjansson.so.4 => /usr/lib64/libjansson.so.4 (0x0000007f94480000)
libc.so.6 => /usr/lib64/libc.so.6 (0x0000007f942d0000)
libpcre2-8.so.0 => /usr/lib64/libpcre2-8.so.0 (0x0000007f94220000)
/lib/ld-linux-aarch64.so.1 (0x0000007f94821000)
libffi.so.8 => /usr/lib64/libffi.so.8 (0x0000007f941f0000)
libz.so.1 => /usr/lib64/libz.so.1 (0x0000007f941b0000)
liblzma.so.5 => /usr/lib64/liblzma.so.5 (0x0000007f94160000)
libm.so.6 => /usr/lib64/libm.so.6 (0x0000007f940b0000)

Discussed in https://gitlab.gnome.org/GNOME/glib/-/issues/3514.

Here’s my finalize code:

static void jmnd_vm_finalize(GObject *object)
{
	jmndVM *vm = JMND_VM(object);

	jmnd_vm_del_async_timer(vm);
	g_object_unref(vm->devices);
	g_object_unref(vm->topo_list);
	g_object_unref(vm->topo_cfg);
	g_object_unref(vm->ctrlq_table);
	g_safe_free(vm->name);
	g_safe_free(vm->uuid);
	G_OBJECT_CLASS(jmnd_vm_parent_class)->finalize(object);

	pthread_mutex_lock(&g_vm_id->lock.lock);
	if (vm->vm_id)
		g_queue_push_head(g_vm_id->queue, vm->vm_id);
	pthread_mutex_unlock(&g_vm_id->lock.lock);
	g_safe_free(vm->create_xml);
}

The code does GObjectClass.finalize, but there is a memory release after that, does it have to do with the order of execution, can GObjectClass.finalize only be executed at the end?
And should the multithreaded g_queue_push_head be replaced with g_async_queue_push to be safer?

It’s generally best practice to put the chain-up call at the end of your finalize function. I don’t see a reason for it to be otherwise here?

I’m not sure that will fix your problem, but it’s worth trying.

You’re chaining up to the parent’s implementation of GObjectClass.finalize():

and then you’re accessing the instance data:

This is undefined behaviour: the finalize() implementation inside GObject will call g_type_free_instance() which will free the memory allocated for the instance structure. Any access to that memory is now invalid, and anything can happen.

You need to chain up at the end of the function, or at least after any and all accesses to the instance structure.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.