Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <aDcfxEXp-r2bRCaJ@voyager>
Date: Wed, 28 May 2025 16:37:56 +0200
From: Markus Wichmann <nullplan@....net>
To: musl@...ts.openwall.com
Subject: Re: Deadlock in dynamic linker?

Am Tue, May 27, 2025 at 07:14:15PM +0200 schrieb Thorsten Glaser:
> On Tue, 27 May 2025, Markus Wichmann wrote:
> 
> >But __libc_exit_fini() refuses to destroy libraries that haven't been
> >constructed completely. If p->constructed is zero, a node is skipped
> 
> Isn’t that the *good* thing?
> 

Sometimes the things I say actually are in relation to the things I
quote. In this case, I suggested to put nodes in the list only when
construction has finished, then Rich said this can't be done, among
other reasons because it may cause __libc_exit_fini() to miss partially
constructed libraries, and then I remarked, that partially constructed
libraries are skipped anyway. I wasn't arguing against skipping them,
merely that the concern was invalid.

But I think I get it now. By putting the node on the list early, we
ensure that __libc_exit_fini() waits for the library to be completely
constructed as soon as the first constructor is run. Unless exit() is
called from such a constructor (which can be recursively called from a
constructor inside a dlopen()ed library).

> >By adding nodes to the fini list only when construction has finished, we
> >would also get rid of the need to have __libc_exit_fini() wait for
> >construction to finish first.
> 
> AIUI that may change the order which is inacceptable?

The exact order of nodes on the list is unimportant. What is important
is that dependencies not appear on it in front of their dependents. And
because that is the exact opposite of the order the constructor calls
must be in, we currently just push nodes to the list in LIFO fashion to
invert the order. Thing is, I am not at all sure this leads to the
correct order.

In the scenario I made up, if we leave the concurrent exit call aside,
liba is opening libb dynamically in the constructor. It would not be
surprising to see liba actually use that library handle to do things
such as call functions from libb, and I don't see why they shouldn't.
Possibly even a liba destructor could do such a thing. But the current
behavior will have libb on the fini list in front of liba, causing liba
to call libb functions after the latter has been destroyed.

I don't really know how to remedy this, and nor whether it is a common
scenario out in the world. Nor do I know how to look up if any spec
actually limits what destructors can do. The gABI I have seen does allow
the implementation to place arbitrary limits on pre-init functions, but
says nothing about initializers or finalizers in that regard.

Ciao,
Markus

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.