[PATCH v2] elf: Fix DTV gap reuse logic (BZ #27135)
Szabolcs Nagy
szabolcs.nagy@arm.com
Fri Jul 9 10:26:37 GMT 2021
The 07/08/2021 13:07, Adhemerval Zanella via Libc-alpha wrote:
> This is updated version of the 572bd547d57a (reverted by 40ebfd016ad2)
> that fixes the _dl_next_tls_modid issues.
>
> This issue with 572bd547d57a patch is the DTV entry will be only
> update on dl_open_worker() with the update_tls_slotinfo() call after
> all dependencies are being processed by _dl_map_object_deps(). However
> _dl_map_object_deps() itself might call _dl_next_tls_modid(), and since
> the _dl_tls_dtv_slotinfo_list::map is not yet set the entry will be
> wrongly reused.i
spurious .i
>
> This patch fixes by renaming the _dl_next_tls_modid() function to
> _dl_assign_tls_modid() and by passing the link_map so it can set
> the slotinfo value so a so subsequente _dl_next_tls_modid() call will
> see the entry as allocated.
.. so a subsequent ..
i think you want to reference the bug number explicitly
to update bugzilla on commit (i think subject line is not
enough) e.g. This patch fixes bug 27135 ..
>
> The intermediary value is cleared up on remove_slotinfo() for the case
> a library fails to load with RTLD_NOW.
>
> Checked on x86_64-linux-gnu.
i have some comments and skipped the tests.
...
> diff --git a/elf/dl-close.c b/elf/dl-close.c
> index 3720e47dd1..f39001cab9 100644
> --- a/elf/dl-close.c
> +++ b/elf/dl-close.c
> @@ -77,8 +77,6 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp,
> object that wasn't fully set up. */
> if (__glibc_likely (old_map != NULL))
> {
> - assert (old_map->l_tls_modid == idx);
> -
i think this assert should be harmless.
(but not very useful so probably ok to remove:
we should check old_map == imap if anything,
but imap is not available here).
> /* Mark the entry as unused. These can be read concurrently. */
> atomic_store_relaxed (&listp->slotinfo[idx - disp].gen,
> GL(dl_tls_generation) + 1);
> @@ -88,7 +86,11 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp,
> /* If this is not the last currently used entry no need to look
> further. */
> if (idx != GL(dl_tls_max_dtv_idx))
> - return true;
> + {
> + /* There is an unused dtv entry in the middle. */
> + GL(dl_tls_dtv_gaps) = true;
> + return true;
> + }
OK.
> }
>
> while (idx - disp > (disp == 0 ? 1 + GL(dl_tls_static_nelem) : 0))
> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index a08df001af..650e4edc35 100644
> --- a/elf/dl-load.c
> +++ b/elf/dl-load.c
> @@ -1498,7 +1498,7 @@ cannot enable executable stack as shared object requires");
> not set up TLS data structures, so don't use them now. */
> || __glibc_likely (GL(dl_tls_dtv_slotinfo_list) != NULL)))
> /* Assign the next available module ID. */
> - l->l_tls_modid = _dl_next_tls_modid ();
> + _dl_assign_tls_modid (l);
OK.
>
> #ifdef DL_AFTER_LOAD
> DL_AFTER_LOAD (l);
> diff --git a/elf/dl-open.c b/elf/dl-open.c
> index a066f39bd0..d2240d8747 100644
> --- a/elf/dl-open.c
> +++ b/elf/dl-open.c
> @@ -899,16 +899,6 @@ no more namespaces available for dlmopen()"));
> state if relocation failed, for example. */
> if (args.map)
> {
> - /* Maybe some of the modules which were loaded use TLS.
> - Since it will be removed in the following _dl_close call
> - we have to mark the dtv array as having gaps to fill the
> - holes. This is a pessimistic assumption which won't hurt
> - if not true. There is no need to do this when we are
> - loading the auditing DSOs since TLS has not yet been set
> - up. */
> - if ((mode & __RTLD_AUDIT) == 0)
> - GL(dl_tls_dtv_gaps) = true;
> -
> _dl_close_worker (args.map, true);
OK.
>
> /* All l_nodelete_pending objects should have been deleted
> diff --git a/elf/dl-tls.c b/elf/dl-tls.c
> index 2b5161d10a..6ff94a67af 100644
> --- a/elf/dl-tls.c
> +++ b/elf/dl-tls.c
> @@ -126,8 +126,8 @@ oom (void)
> }
>
>
> -size_t
> -_dl_next_tls_modid (void)
> +void
> +_dl_assign_tls_modid (struct link_map *l)
> {
> size_t result;
>
> @@ -157,7 +157,11 @@ _dl_next_tls_modid (void)
> }
>
> if (result - disp < runp->len)
> - break;
> + {
> + /* Mark the entry as used, so any dependency see it. */
> + runp->slotinfo[result - disp].map = l;
this works, but i'd use relaxed atomic store
since this store can actually be in a data race
with the load in _dl_update_slotinfo.
(i tried to follow the principle in my patchset
that racy access is marked atomic even if it is
benign)
it is also possible to change _dl_update_slotinfo
such that it only reads .map for the given modid
and then there is no race and no atomics is needed
on .map, but that should be done separately.
> + break;
> + }
>
> disp += runp->len;
> }
> @@ -184,17 +188,14 @@ _dl_next_tls_modid (void)
> atomic_store_relaxed (&GL(dl_tls_max_dtv_idx), result);
> }
>
> - return result;
> + l->l_tls_modid = result;
> }
OK.
>
>
> size_t
> _dl_count_modids (void)
> {
> - /* It is rare that we have gaps; see elf/dl-open.c (_dl_open) where
> - we fail to load a module and unload it leaving a gap. If we don't
> - have gaps then the number of modids is the current maximum so
> - return that. */
> + /* The count is the max unless dlclose or failed dlopen created gaps. */
> if (__glibc_likely (!GL(dl_tls_dtv_gaps)))
> return GL(dl_tls_max_dtv_idx);
OK.
>
> diff --git a/elf/rtld.c b/elf/rtld.c
> index fbbd60b446..160faaf5ab 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -1722,7 +1722,7 @@ dl_main (const ElfW(Phdr) *phdr,
> /* Add the dynamic linker to the TLS list if it also uses TLS. */
> if (GL(dl_rtld_map).l_tls_blocksize != 0)
> /* Assign a module ID. Do this before loading any audit modules. */
> - GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid ();
> + _dl_assign_tls_modid (&GL(dl_rtld_map));
OK.
>
> audit_list_add_dynamic_tag (&state.audit_list, main_map, DT_AUDIT);
> audit_list_add_dynamic_tag (&state.audit_list, main_map, DT_DEPAUDIT);
...
> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index 176394de4d..9c15259236 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -1171,8 +1171,8 @@ extern ElfW(Addr) _dl_sysdep_start (void **start_argptr,
> extern void _dl_sysdep_start_cleanup (void) attribute_hidden;
>
>
> -/* Determine next available module ID. */
> -extern size_t _dl_next_tls_modid (void) attribute_hidden;
> +/* Determine next available module ID and set the L l_tls_modid. */
> +extern void _dl_assign_tls_modid (struct link_map *l) attribute_hidden;
OK.
>
> /* Count the modules with TLS segments. */
> extern size_t _dl_count_modids (void) attribute_hidden;
> --
> 2.30.2
>
More information about the Libc-alpha
mailing list