public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* Crash when dlclosing oneself in a global destructor
@ 2016-04-05 18:11 Geoffrey Thomas
  2016-04-07 20:15 ` Adhemerval Zanella
  0 siblings, 1 reply; 2+ messages in thread
From: Geoffrey Thomas @ 2016-04-05 18:11 UTC (permalink / raw)
  To: libc-alpha

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

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: Crash when dlclosing oneself in a global destructor
  2016-04-05 18:11 Crash when dlclosing oneself in a global destructor Geoffrey Thomas
@ 2016-04-07 20:15 ` Adhemerval Zanella
  0 siblings, 0 replies; 2+ messages in thread
From: Adhemerval Zanella @ 2016-04-07 20:15 UTC (permalink / raw)
  To: libc-alpha



On 05-04-2016 15:11, Geoffrey Thomas wrote:
> 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.)
> 


I think it has been already discussed here [1] and I think we agreed
this is allowed by POSIX (look it has been tracked by BZ#14697).
However no patch has been proposed yet and I am not sure that just
adding flag to set if l_fini is already being called is the right
fix to it (check bug report comments).

[1] https://www.sourceware.org/ml/libc-alpha/2015-09/msg00536.html
[2] https://sourceware.org/bugzilla/show_bug.cgi?id=14697

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2016-04-07 20:15 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-05 18:11 Crash when dlclosing oneself in a global destructor Geoffrey Thomas
2016-04-07 20:15 ` Adhemerval Zanella

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).