[PATCH] elf: Release dl_load_lock before running dlopen constructors (BZ 15686)
Artem Proskurnev
temap@mail.ru
Mon Jul 13 23:00:00 GMT 2026
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/20260713225303.4096454-1-temap@mail.ru/
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
>
More information about the Libc-alpha
mailing list