elf: Implement force_first handling in _dl_sort_maps_dfs (bug 28937)

The implementation in _dl_close_worker requires that the first
element of l_initfini is always this very map (“We are always the
zeroth entry, and since we don't include ourselves in the
dependency analysis start at 1.”).  Rather than fixing that
assumption, this commit adds an implementation of the force_first
argument to the new dependency sorting algorithm.  This also means
that the directly dlopen'ed shared object is always initialized last,
which is the least surprising behavior in the presence of cycles.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit 1df71d32fe)
This commit is contained in:
Florian Weimer 2022-09-20 11:00:42 +02:00
parent c15c0f1985
commit d883d02008
3 changed files with 31 additions and 9 deletions

1
NEWS
View File

@ -24,6 +24,7 @@ The following bugs are resolved with this release:
missing libraries
[28896] strncmp-avx2-rtm and wcsncmp-avx2-rtm fallback on non-rtm
variants when avoiding overflow
[28937] New DSO dependency sorter does not put new map first if in a cycle
[28953] nss: Protect against errno changes in function lookup
[29029] nptl: poll() spuriously returns EINTR during thread
cancellation and with cancellation disabled

View File

@ -182,8 +182,9 @@ dfs_traversal (struct link_map ***rpo, struct link_map *map,
static void
_dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
bool force_first __attribute__ ((unused)), bool for_fini)
bool force_first, bool for_fini)
{
struct link_map *first_map = maps[0];
for (int i = nmaps - 1; i >= 0; i--)
maps[i]->l_visited = 0;
@ -208,14 +209,6 @@ _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
Adjusting the order so that maps[0] is last traversed naturally avoids
this problem.
Further, the old "optimization" of skipping the main object at maps[0]
from the call-site (i.e. _dl_sort_maps(maps+1,nmaps-1)) is in general
no longer valid, since traversing along object dependency-links
may "find" the main object even when it is not included in the initial
order (e.g. a dlopen()'ed shared object can have circular dependencies
linked back to itself). In such a case, traversing N-1 objects will
create a N-object result, and raise problems.
To summarize, just passing in the full list, and iterating from back
to front makes things much more straightforward. */
@ -274,6 +267,27 @@ _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps,
}
memcpy (maps, rpo, sizeof (struct link_map *) * nmaps);
/* Skipping the first object at maps[0] is not valid in general,
since traversing along object dependency-links may "find" that
first object even when it is not included in the initial order
(e.g., a dlopen'ed shared object can have circular dependencies
linked back to itself). In such a case, traversing N-1 objects
will create a N-object result, and raise problems. Instead,
force the object back into first place after sorting. This naive
approach may introduce further dependency ordering violations
compared to rotating the cycle until the first map is again in
the first position, but as there is a cycle, at least one
violation is already present. */
if (force_first && maps[0] != first_map)
{
int i;
for (i = 0; maps[i] != first_map; ++i)
;
assert (i < nmaps);
memmove (&maps[1], maps, i * sizeof (maps[0]));
maps[0] = first_map;
}
}
void

View File

@ -64,3 +64,10 @@ output: b>a>{}<a<b
tst-bz15311: {+a;+e;+f;+g;+d;%d;-d;-g;-f;-e;-a};a->b->c->d;d=>[ba];c=>a;b=>e=>a;c=>f=>b;d=>g=>c
output(glibc.rtld.dynamic_sort=1): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<c<d<g<f<b<e];}
output(glibc.rtld.dynamic_sort=2): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<g<f<a<b<c<d<e];}
# Test that even in the presence of dependency loops involving dlopen'ed
# object, that object is initialized last (and not unloaded prematurely).
# Final destructor order is indeterminate due to the cycle.
tst-bz28937: {+a;+b;-b;+c;%c};a->a1;a->a2;a2->a;b->b1;c->a1;c=>a1
output(glibc.rtld.dynamic_sort=1): {+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}<a<a2<c<a1
output(glibc.rtld.dynamic_sort=2): {+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}<a2<a<c<a1