public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2] elf: Implement force_first handling in _dl_sort_maps_dfs (bug 28937)
@ 2022-09-06  7:01 Florian Weimer
  2022-09-19 18:31 ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 2+ messages in thread
From: Florian Weimer @ 2022-09-06  7:01 UTC (permalink / raw)
  To: libc-alpha

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.

---
v2: Incorporate Adhemerval's review comments.  Retested on i386-linux-gnu
    and x86_64-linux-gnu.
 elf/dl-sort-maps.c       | 32 +++++++++++++++++++++++---------
 elf/dso-sort-tests-1.def |  7 +++++++
 2 files changed, 30 insertions(+), 9 deletions(-)

diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c
index 5b550b1e94..3e2a6a584e 100644
--- a/elf/dl-sort-maps.c
+++ b/elf/dl-sort-maps.c
@@ -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 5f7f18ef27..4bf9052db1 100644
--- a/elf/dso-sort-tests-1.def
+++ b/elf/dso-sort-tests-1.def
@@ -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

base-commit: dbb75513f5cf9285c77c9e55777c5c35b653f890


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH v2] elf: Implement force_first handling in _dl_sort_maps_dfs (bug 28937)
  2022-09-06  7:01 [PATCH v2] elf: Implement force_first handling in _dl_sort_maps_dfs (bug 28937) Florian Weimer
@ 2022-09-19 18:31 ` Adhemerval Zanella Netto
  0 siblings, 0 replies; 2+ messages in thread
From: Adhemerval Zanella Netto @ 2022-09-19 18:31 UTC (permalink / raw)
  To: Florian Weimer, libc-alpha



On 06/09/22 04:01, Florian Weimer via Libc-alpha wrote:
> 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.

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> 
> ---
> v2: Incorporate Adhemerval's review comments.  Retested on i386-linux-gnu
>     and x86_64-linux-gnu.
>  elf/dl-sort-maps.c       | 32 +++++++++++++++++++++++---------
>  elf/dso-sort-tests-1.def |  7 +++++++
>  2 files changed, 30 insertions(+), 9 deletions(-)
> 
> diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c
> index 5b550b1e94..3e2a6a584e 100644
> --- a/elf/dl-sort-maps.c
> +++ b/elf/dl-sort-maps.c
> @@ -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 5f7f18ef27..4bf9052db1 100644
> --- a/elf/dso-sort-tests-1.def
> +++ b/elf/dso-sort-tests-1.def
> @@ -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
> 
> base-commit: dbb75513f5cf9285c77c9e55777c5c35b653f890
> 

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2022-09-19 18:31 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-06  7:01 [PATCH v2] elf: Implement force_first handling in _dl_sort_maps_dfs (bug 28937) Florian Weimer
2022-09-19 18:31 ` Adhemerval Zanella Netto

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).