Re: [PATCH] elf: Release dl_load_lock before running dlopen constructors (BZ 15686)
Artem Proskurnev <[email protected]>
| Newsgroups | gmane.comp.lib.glibc.alpha |
|---|---|
| Message-ID | <[email protected]> |
I tried to consider all possible problems and added changes to pthread_once to the patch. I recompiled everything and checked all the tests using make check. https://inbox.sourceware.org/libc-alpha/[email protected]/ 13.07.2026 08:43, Florian Weimer пишет: >> diff --git a/elf/dl-open.c b/elf/dl-open.c >> index cf4749694f..2e824d86bc 100644 >> --- a/elf/dl-open.c >> +++ b/elf/dl-open.c >> @@ -792,11 +792,37 @@ dl_open_worker (void *a) >> int mode = args->mode; >> struct link_map *new = args->map; >> >> + /* The link_map structures are fully initialized at this point. We >> + can release dl_load_lock so that constructors can safely spawn >> + threads without deadlocking on dl_load_lock (e.g. if the new thread >> + accesses a thread_local variable and calls __cxa_thread_atexit_impl, >> + or performs operations that trigger NSS module loading). >> + >> + The DSO has l_direct_opencount == 1 at this point, so it cannot be >> + unloaded by a concurrent dlclose -- _dl_close_worker skips objects >> + with nonzero opencount, and l_map_used marking transitively protects >> + implicit dependencies. Concurrent dlopen and dlclose in other >> + threads are not blocked while the lock is released, but they are >> + safe: our DSO's link_map structures are fully initialized and >> + immutable at this point. >> + >> + Notably, dl_iterate_phdr (which uses dl_load_write_lock, not >> + dl_load_lock) can observe the DSO before its constructor has run. >> + This is consistent with POSIX, which does not guarantee that a >> + dlopen'd DSO appears atomically -- and is no different from dlsym >> + inside a constructor observing partially-initialized symbols from >> + the main executable. */ >> + __rtld_lock_unlock_recursive (GL(dl_load_lock)); >> + >> /* Run the initializer functions of new objects. Temporarily >> disable the exception handler, so that lazy binding failures are >> fatal. */ >> _dl_catch_exception (NULL, call_dl_init, args); >> >> + /* Re-acquire dl_load_lock for the final global scope update and for >> + the lock/unlock pairing expected by _dl_open. */ >> + __rtld_lock_lock_recursive (GL(dl_load_lock)); >> + > I believe this results in a data race on l_init_called. In a > multi-threaded application, the same ELF constructor may execute with > itself in parallel on different threads, or dlopen may return before the > ELF constructor has completed running in another thread. Some form of > synchronization is required to deal with these scenarios, perhaps using > pthread_once or a condition variable. > > We also likely cannot remove a lock like this for old applications which > may depend on its existence. We can deal with this with a different > mechanism. > > Thanks, > Florian >