This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
Crash when dlclosing oneself in a global destructor
- From: Geoffrey Thomas <geofft at ldpreload dot com>
- To: libc-alpha at sourceware dot org
- Date: Tue, 5 Apr 2016 14:11:33 -0400 (EDT)
- Subject: Crash when dlclosing oneself in a global destructor
- Authentication-results: sourceware.org; auth=none
Hi folks,
I'm using a (proprietary) app at $DAYJOB that is triggering a glibc
assertion failure when it shuts down. I fully admit that what it's doing
is bizarre, but I don't think it's so bizarre that it's reasonable for
glibc to crash the process in this scenario. Here's a minimal reproducer:
titan:/tmp geofft$ cat libstupid.c
#include <dlfcn.h>
void *handle = 0;
__attribute__((constructor))
void c(void) {
handle = dlopen("/tmp/libstupid.so",
RTLD_GLOBAL | RTLD_LAZY | RTLD_NODELETE);
}
__attribute__((destructor))
void d(void) {
dlclose(handle);
}
titan:/tmp geofft$ cat hello.c
#include <stdio.h>
int main(void) {
printf("Hello world!\n");
return 0;
}
titan:/tmp geofft$ cc -fPIC -shared -o libstupid.so libstupid.c -ldl
titan:/tmp geofft$ cc -L. -o hello hello.c -lstupid -Wl,-rpath,/tmp
titan:/tmp geofft$ ./hello
Hello world!
Inconsistency detected by ld.so: dl-close.c: 762: _dl_close: Assertion `map->l_init_called' failed!
titan:/tmp geofft$
It looks like what's happening is that map->l_init_called is being set to
0 in _dl_fini (with comment "Make sure nothing happens if we are called
twice"), before it runs global destructors, but _dl_close has the
following check:
/* First see whether we can remove the object at all. */
if (__glibc_unlikely (map->l_flags_1 & DF_1_NODELETE))
{
assert (map->l_init_called);
/* Nope. Do nothing. */
return;
}
I'm not entirely sure what the purpose of this assertion is, but if it's
useful, at the least _dl_fini should flip some flag other than
l_init_called. Maybe add a new l_fini_called?
See also
https://www.sourceware.org/ml/libc-alpha/2015-09/msg00536.html
https://bugzilla.redhat.com/show_bug.cgi?id=1264556
http://savannah.gnu.org/bugs/?34195
for some different cases of people running up against this assertion
(in somewhat different contexts) and getting confused.
(The actual app in question has a library that dlopens a list of shared
libraries, which happens to include the library that does the dlopening. I
don't think it's particularly trying to dlopen itself, but does so sort
of by accident.)
--
Geoffrey Thomas
https://ldpreload.com
geofft@ldpreload.com