GNOME Bugzilla – Bug 791462
glib creates thread-local data in init function, causing a crash if the lib is dlclose()d
Last modified: 2018-05-24 19:59:10 UTC
The glib_init constructor function indirectly creates a thread-specific data key, as shown here: * frame #0: 0x00007fff6d7a5a15 libsystem_pthread.dylib`pthread_key_create frame #1: 0x000000010c880f8a libglib-2.0.0.dylib`g_private_get_impl + 71 frame #2: 0x000000010c880f3a libglib-2.0.0.dylib`g_private_get + 9 frame #3: 0x000000010c85d137 libglib-2.0.0.dylib`thread_memory_from_self + 37 frame #4: 0x000000010c85cbb0 libglib-2.0.0.dylib`g_slice_alloc + 25 frame #5: 0x000000010c836c8e libglib-2.0.0.dylib`g_hash_table_new_full + 36 frame #6: 0x000000010c852768 libglib-2.0.0.dylib`g_quark_init + 32 frame #7: 0x00000001017b8a0a dyld`ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 420 (presumably missing stack frames for glib_init and glib_init_ctor due to optimizations) This creates a problem if glib is loaded using dlopen(), and later closed using dlclose(). It would be fairly unusual to do so directly, but I ran into it when dlopen()ing and later dlclose()ing a library which depends on glib, which in turn opens and closes glib itself. Since glib is no longer mapped, the destructor function for the thread-specific key no longer exists, and the process crashes when the thread exits and its TSS keys are cleaned up. Additionally, glib's constructor function appears to allocate data that is not freed when the lib is unloaded, which means that opening and closing the library leaks memory. Recommended solution: - During the library constructor, either free any thread-specific data allocated after finished with it, or don't use any to begin with. - Add a destructor that frees any data allocated in the constructor.
We explicitly do not support dlclose() on GObject or libraries which use it (such as GIO). See bug #733065. *** This bug has been marked as a duplicate of bug 733065 ***
You mentioned libgobject, but I'm referring to libglib (which does not depend on libgobject; in fact, the vice versa is true). Is dlclose()ing libglib also explicitly unsupported? If so, shouldn't -Wl,-z,nodelete be passed to ld there as well?
(In reply to Rodger Combs from comment #2) > You mentioned libgobject, but I'm referring to libglib (which does not > depend on libgobject; in fact, the vice versa is true). Is dlclose()ing > libglib also explicitly unsupported? If so, shouldn't -Wl,-z,nodelete be > passed to ld there as well? Are you sure libgobject isn’t linked in at all? It’s fairly rare for libglib to be used without libgobject. To be honest, I’m not sure about what guarantees we give for dlopen()ing libglib itself. The allocations in this case are for the GSlice allocator’s maps, and for the GQuark mapping table. Colin, do you know the history of this? At the least, I think the policy on dlopen() support for libg* should be written down somewhere.
I think we should just extend the 'explicitly don't support dlclose()' to libglib as well.
(In reply to Philip Withnall from comment #3) > (In reply to Rodger Combs from comment #2) > > You mentioned libgobject, but I'm referring to libglib (which does not > > depend on libgobject; in fact, the vice versa is true). Is dlclose()ing > > libglib also explicitly unsupported? If so, shouldn't -Wl,-z,nodelete be > > passed to ld there as well? > > Are you sure libgobject isn’t linked in at all? It’s fairly rare for libglib > to be used without libgobject. There are some cases, still, especially on the server side of things, where GLib is used as a better C standard library. To be fair, though, it's really, *really* rare to dlopen() GLib alone, whereas dlopen()'ing GObject for GUI plugins is much more widespread. > To be honest, I’m not sure about what guarantees we give for dlopen()ing > libglib itself. The allocations in this case are for the GSlice allocator’s > maps, and for the GQuark mapping table. GLib should *probably* allow unloading; we can safely destroy the slice allocator's configuration, as well as the interned strings table. There's the problem of memory allocated through GLib, or pointers to cached data, surviving the dlclose(); things would get real ugly real fast. > Colin, do you know the history of this? At the least, I think the policy on > dlopen() support for libg* should be written down somewhere. Indeed. We kind of encoded this in Bugzilla and the commit log, but there's no actual policy; we always hoped to make the GObject type system resilient to these cases, but in practice everything that declares a static GType cannot ever be unloaded. We should add an entry to README.rationale.
(In reply to Matthias Clasen from comment #4) > I think we should just extend the 'explicitly don't support dlclose()' to > libglib as well. That'd certainly be easier — and if we ever merge all the glib shared libraries into a single one, like Alex has wanted for years — then it'd also be the equivalent end result. Whatever we choose, we should definitely document it; I've had to explain this on IRC at least a couple of times per year.
libgobject was not loaded in this case. libglib was linked by libharfbuzz (not libharfbuzz-gobject), in turn loaded by libass, in turn loaded by a plugin object I'd loaded and then subsequently closed.
-- GitLab Migration Automatic Message -- This bug has been migrated to GNOME's GitLab instance and has been closed from further activity. You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/glib/issues/1311.