| commit 1df71d32fe5f5905ffd5d100e5e9ca8ad6210891 |
| Author: Florian Weimer <fweimer@redhat.com> |
| Date: Tue Sep 20 11:00:42 2022 +0200 |
| |
| 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> |
| |
| diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c |
| index 7c614bca5d8115c6..e8ef5e8b3588ab53 100644 |
| |
| |
| @@ -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 |
| diff --git a/elf/dso-sort-tests-1.def b/elf/dso-sort-tests-1.def |
| index 5f7f18ef270bc12d..4bf9052db16fb352 100644 |
| |
| |
| @@ -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 |