83f30e
commit 3a0588ae48fb35384a6bd33f9b66403badfa1262
83f30e
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
83f30e
Date:   Tue Feb 8 15:22:49 2022 -0300
83f30e
83f30e
    elf: Fix DFS sorting algorithm for LD_TRACE_LOADED_OBJECTS with missing libraries (BZ #28868)
83f30e
83f30e
    On _dl_map_object the underlying file is not opened in trace mode
83f30e
    (in other cases where the underlying file can't be opened,
83f30e
    _dl_map_object  quits with an error).  If there any missing libraries
83f30e
    being processed, they will not be considered on final nlist size
83f30e
    passed on _dl_sort_maps later in the function.  And it is then used by
83f30e
    _dl_sort_maps_dfs on the stack allocated working maps:
83f30e
83f30e
    222   /* Array to hold RPO sorting results, before we copy back to  maps[].  */
83f30e
    223   struct link_map *rpo[nmaps];
83f30e
    224
83f30e
    225   /* The 'head' position during each DFS iteration. Note that we start at
83f30e
    226      one past the last element due to first-decrement-then-store (see the
83f30e
    227      bottom of above dfs_traversal() routine).  */
83f30e
    228   struct link_map **rpo_head = &rpo[nmaps];
83f30e
83f30e
    However while transversing the 'l_initfini' on dfs_traversal it will
83f30e
    still consider the l_faked maps and thus update rpo more times than the
83f30e
    allocated working 'rpo', overflowing the stack object.
83f30e
83f30e
    As suggested in bugzilla, one option would be to avoid sorting the maps
83f30e
    for trace mode.  However I think ignoring l_faked object does make
83f30e
    sense (there is one less constraint to call the sorting function), it
83f30e
    allows a slight less stack usage for trace, and it is slight simpler
83f30e
    solution.
83f30e
83f30e
    The tests does trigger the stack overflow, however I tried to make
83f30e
    it more generic to check different scenarios or missing objects.
83f30e
83f30e
    Checked on x86_64-linux-gnu.
83f30e
83f30e
    Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
83f30e
83f30e
Conflicts:
83f30e
	elf/Makefile
83f30e
	  (differences in backported tests)
83f30e
83f30e
diff --git a/elf/Makefile b/elf/Makefile
83f30e
index 22a8060f7d3bb1a1..634c3113227d64a6 100644
83f30e
--- a/elf/Makefile
83f30e
+++ b/elf/Makefile
83f30e
@@ -584,6 +584,11 @@ modules-names = \
83f30e
   libmarkermod5-3 \
83f30e
   libmarkermod5-4 \
83f30e
   libmarkermod5-5 \
83f30e
+  libtracemod1-1 \
83f30e
+  libtracemod2-1 \
83f30e
+  libtracemod3-1 \
83f30e
+  libtracemod4-1 \
83f30e
+  libtracemod5-1 \
83f30e
   ltglobmod1 \
83f30e
   ltglobmod2 \
83f30e
   neededobj1 \
83f30e
@@ -983,6 +988,11 @@ tests-special += \
83f30e
   $(objpfx)tst-initorder2-cmp.out \
83f30e
   $(objpfx)tst-unused-dep-cmp.out \
83f30e
   $(objpfx)tst-unused-dep.out \
83f30e
+  $(objpfx)tst-trace1.out \
83f30e
+  $(objpfx)tst-trace2.out \
83f30e
+  $(objpfx)tst-trace3.out \
83f30e
+  $(objpfx)tst-trace4.out \
83f30e
+  $(objpfx)tst-trace5.out \
83f30e
   # tests-special
83f30e
 endif
83f30e
 
83f30e
@@ -2619,6 +2629,51 @@ $(objpfx)tst-rtld-run-static.out: $(objpfx)/ldconfig
83f30e
 
83f30e
 $(objpfx)tst-dlmopen-gethostbyname: $(libdl)
83f30e
 $(objpfx)tst-dlmopen-gethostbyname.out: $(objpfx)tst-dlmopen-gethostbyname-mod.so
83f30e
+
83f30e
+LDFLAGS-libtracemod1-1.so += -Wl,-soname,libtracemod1.so
83f30e
+LDFLAGS-libtracemod2-1.so += -Wl,-soname,libtracemod2.so
83f30e
+LDFLAGS-libtracemod3-1.so += -Wl,-soname,libtracemod3.so
83f30e
+LDFLAGS-libtracemod4-1.so += -Wl,-soname,libtracemod4.so
83f30e
+LDFLAGS-libtracemod5-1.so += -Wl,-soname,libtracemod5.so
83f30e
+
83f30e
+$(objpfx)libtracemod1-1.so: $(objpfx)libtracemod2-1.so \
83f30e
+			    $(objpfx)libtracemod3-1.so
83f30e
+$(objpfx)libtracemod2-1.so: $(objpfx)libtracemod4-1.so \
83f30e
+			    $(objpfx)libtracemod5-1.so
83f30e
+
83f30e
+define libtracemod-x
83f30e
+$(objpfx)libtracemod$(1)/libtracemod$(1).so: $(objpfx)libtracemod$(1)-1.so
83f30e
+	$$(make-target-directory)
83f30e
+	cp $$< $$@
83f30e
+endef
83f30e
+libtracemod-suffixes = 1 2 3 4 5
83f30e
+$(foreach i,$(libtracemod-suffixes), $(eval $(call libtracemod-x,$(i))))
83f30e
+
83f30e
+define tst-trace-skeleton
83f30e
+$(objpfx)tst-trace$(1).out: $(objpfx)libtracemod1/libtracemod1.so \
83f30e
+			    $(objpfx)libtracemod2/libtracemod2.so \
83f30e
+			    $(objpfx)libtracemod3/libtracemod3.so \
83f30e
+			    $(objpfx)libtracemod4/libtracemod4.so \
83f30e
+			    $(objpfx)libtracemod5/libtracemod5.so \
83f30e
+			    $(..)scripts/tst-ld-trace.py \
83f30e
+			    tst-trace$(1).exp
83f30e
+	${ $(PYTHON) $(..)scripts/tst-ld-trace.py \
83f30e
+	    "$(test-wrapper-env) $(elf-objpfx)$(rtld-installed-name) \
83f30e
+	    --library-path $(common-objpfx):$(strip $(2)) \
83f30e
+	    $(objpfx)libtracemod1/libtracemod1.so" tst-trace$(1).exp \
83f30e
+	} > $$@; $$(evaluate-test)
83f30e
+endef
83f30e
+
83f30e
+$(eval $(call tst-trace-skeleton,1,))
83f30e
+$(eval $(call tst-trace-skeleton,2,\
83f30e
+	$(objpfx)libtracemod2))
83f30e
+$(eval $(call tst-trace-skeleton,3,\
83f30e
+	$(objpfx)libtracemod2:$(objpfx)libtracemod3))
83f30e
+$(eval $(call tst-trace-skeleton,4,\
83f30e
+	$(objpfx)libtracemod2:$(objpfx)libtracemod3:$(objpfx)libtracemod4))
83f30e
+$(eval $(call tst-trace-skeleton,5,\
83f30e
+	$(objpfx)libtracemod2:$(objpfx)libtracemod3:$(objpfx)libtracemod4:$(objpfx)libtracemod5))
83f30e
+
83f30e
 $(objpfx)tst-audit-tlsdesc: $(objpfx)tst-audit-tlsdesc-mod1.so \
83f30e
 			    $(objpfx)tst-audit-tlsdesc-mod2.so \
83f30e
 			    $(shared-thread-library)
83f30e
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
83f30e
index 9365d54c8e03e5f4..9ff589c8562b2dd1 100644
83f30e
--- a/elf/dl-deps.c
83f30e
+++ b/elf/dl-deps.c
83f30e
@@ -489,6 +489,8 @@ _dl_map_object_deps (struct link_map *map,
83f30e
 
83f30e
   for (nlist = 0, runp = known; runp; runp = runp->next)
83f30e
     {
83f30e
+      /* _dl_sort_maps ignores l_faked object, so it is safe to not consider
83f30e
+	 them for nlist.  */
83f30e
       if (__builtin_expect (trace_mode, 0) && runp->map->l_faked)
83f30e
 	/* This can happen when we trace the loading.  */
83f30e
 	--map->l_searchlist.r_nlist;
83f30e
diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c
83f30e
index 398a08f28c4d9ff1..99354dc08a010dd3 100644
83f30e
--- a/elf/dl-sort-maps.c
83f30e
+++ b/elf/dl-sort-maps.c
83f30e
@@ -140,7 +140,9 @@ static void
83f30e
 dfs_traversal (struct link_map ***rpo, struct link_map *map,
83f30e
 	       bool *do_reldeps)
83f30e
 {
83f30e
-  if (map->l_visited)
83f30e
+  /* _dl_map_object_deps ignores l_faked objects when calculating the
83f30e
+     number of maps before calling _dl_sort_maps, ignore them as well.  */
83f30e
+  if (map->l_visited || map->l_faked)
83f30e
     return;
83f30e
 
83f30e
   map->l_visited = 1;
83f30e
diff --git a/elf/libtracemod1-1.c b/elf/libtracemod1-1.c
83f30e
new file mode 100644
83f30e
index 0000000000000000..7c89c9a5a40b9668
83f30e
--- /dev/null
83f30e
+++ b/elf/libtracemod1-1.c
83f30e
@@ -0,0 +1 @@
83f30e
+/* Empty  */
83f30e
diff --git a/elf/libtracemod2-1.c b/elf/libtracemod2-1.c
83f30e
new file mode 100644
83f30e
index 0000000000000000..7c89c9a5a40b9668
83f30e
--- /dev/null
83f30e
+++ b/elf/libtracemod2-1.c
83f30e
@@ -0,0 +1 @@
83f30e
+/* Empty  */
83f30e
diff --git a/elf/libtracemod3-1.c b/elf/libtracemod3-1.c
83f30e
new file mode 100644
83f30e
index 0000000000000000..7c89c9a5a40b9668
83f30e
--- /dev/null
83f30e
+++ b/elf/libtracemod3-1.c
83f30e
@@ -0,0 +1 @@
83f30e
+/* Empty  */
83f30e
diff --git a/elf/libtracemod4-1.c b/elf/libtracemod4-1.c
83f30e
new file mode 100644
83f30e
index 0000000000000000..7c89c9a5a40b9668
83f30e
--- /dev/null
83f30e
+++ b/elf/libtracemod4-1.c
83f30e
@@ -0,0 +1 @@
83f30e
+/* Empty  */
83f30e
diff --git a/elf/libtracemod5-1.c b/elf/libtracemod5-1.c
83f30e
new file mode 100644
83f30e
index 0000000000000000..7c89c9a5a40b9668
83f30e
--- /dev/null
83f30e
+++ b/elf/libtracemod5-1.c
83f30e
@@ -0,0 +1 @@
83f30e
+/* Empty  */
83f30e
diff --git a/elf/tst-trace1.exp b/elf/tst-trace1.exp
83f30e
new file mode 100644
83f30e
index 0000000000000000..4a6f5211a68fe2c8
83f30e
--- /dev/null
83f30e
+++ b/elf/tst-trace1.exp
83f30e
@@ -0,0 +1,4 @@
83f30e
+ld 1
83f30e
+libc 1
83f30e
+libtracemod2.so 0
83f30e
+libtracemod3.so 0
83f30e
diff --git a/elf/tst-trace2.exp b/elf/tst-trace2.exp
83f30e
new file mode 100644
83f30e
index 0000000000000000..e13506e2eb9aeca2
83f30e
--- /dev/null
83f30e
+++ b/elf/tst-trace2.exp
83f30e
@@ -0,0 +1,6 @@
83f30e
+ld 1
83f30e
+libc 1
83f30e
+libtracemod2.so 1
83f30e
+libtracemod3.so 0
83f30e
+libtracemod4.so 0
83f30e
+libtracemod5.so 0
83f30e
diff --git a/elf/tst-trace3.exp b/elf/tst-trace3.exp
83f30e
new file mode 100644
83f30e
index 0000000000000000..e574549d12a53d72
83f30e
--- /dev/null
83f30e
+++ b/elf/tst-trace3.exp
83f30e
@@ -0,0 +1,6 @@
83f30e
+ld 1
83f30e
+libc 1
83f30e
+libtracemod2.so 1
83f30e
+libtracemod3.so 1
83f30e
+libtracemod4.so 0
83f30e
+libtracemod5.so 0
83f30e
diff --git a/elf/tst-trace4.exp b/elf/tst-trace4.exp
83f30e
new file mode 100644
83f30e
index 0000000000000000..31ca97b35bde0009
83f30e
--- /dev/null
83f30e
+++ b/elf/tst-trace4.exp
83f30e
@@ -0,0 +1,6 @@
83f30e
+ld 1
83f30e
+libc 1
83f30e
+libtracemod2.so 1
83f30e
+libtracemod3.so 1
83f30e
+libtracemod4.so 1
83f30e
+libtracemod5.so 0
83f30e
diff --git a/elf/tst-trace5.exp b/elf/tst-trace5.exp
83f30e
new file mode 100644
83f30e
index 0000000000000000..5d7d95372656396f
83f30e
--- /dev/null
83f30e
+++ b/elf/tst-trace5.exp
83f30e
@@ -0,0 +1,6 @@
83f30e
+ld 1
83f30e
+libc 1
83f30e
+libtracemod2.so 1
83f30e
+libtracemod3.so 1
83f30e
+libtracemod4.so 1
83f30e
+libtracemod5.so 1
83f30e
diff --git a/scripts/tst-ld-trace.py b/scripts/tst-ld-trace.py
83f30e
new file mode 100755
83f30e
index 0000000000000000..f5a402800377f44b
83f30e
--- /dev/null
83f30e
+++ b/scripts/tst-ld-trace.py
83f30e
@@ -0,0 +1,108 @@
83f30e
+#!/usr/bin/python3
83f30e
+# Dump the output of LD_TRACE_LOADED_OBJECTS in architecture neutral format.
83f30e
+# Copyright (C) 2022 Free Software Foundation, Inc.
83f30e
+# Copyright The GNU Toolchain Authors.
83f30e
+# This file is part of the GNU C Library.
83f30e
+#
83f30e
+# The GNU C Library is free software; you can redistribute it and/or
83f30e
+# modify it under the terms of the GNU Lesser General Public
83f30e
+# License as published by the Free Software Foundation; either
83f30e
+# version 2.1 of the License, or (at your option) any later version.
83f30e
+#
83f30e
+# The GNU C Library is distributed in the hope that it will be useful,
83f30e
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
83f30e
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
83f30e
+# Lesser General Public License for more details.
83f30e
+#
83f30e
+# You should have received a copy of the GNU Lesser General Public
83f30e
+# License along with the GNU C Library; if not, see
83f30e
+# <https://www.gnu.org/licenses/>.
83f30e
+
83f30e
+import argparse
83f30e
+import os
83f30e
+import subprocess
83f30e
+import sys
83f30e
+
83f30e
+try:
83f30e
+    subprocess.run
83f30e
+except:
83f30e
+    class _CompletedProcess:
83f30e
+        def __init__(self, args, returncode, stdout=None, stderr=None):
83f30e
+            self.args = args
83f30e
+            self.returncode = returncode
83f30e
+            self.stdout = stdout
83f30e
+            self.stderr = stderr
83f30e
+
83f30e
+    def _run(*popenargs, input=None, timeout=None, check=False, **kwargs):
83f30e
+        assert(timeout is None)
83f30e
+        with subprocess.Popen(*popenargs, **kwargs) as process:
83f30e
+            try:
83f30e
+                stdout, stderr = process.communicate(input)
83f30e
+            except:
83f30e
+                process.kill()
83f30e
+                process.wait()
83f30e
+                raise
83f30e
+            returncode = process.poll()
83f30e
+            if check and returncode:
83f30e
+                raise subprocess.CalledProcessError(returncode, popenargs)
83f30e
+        return _CompletedProcess(popenargs, returncode, stdout, stderr)
83f30e
+
83f30e
+    subprocess.run = _run
83f30e
+
83f30e
+def is_vdso(lib):
83f30e
+    return lib.startswith('linux-gate') or lib.startswith('linux-vdso')
83f30e
+
83f30e
+
83f30e
+def parse_trace(cmd, fref):
83f30e
+    new_env = os.environ.copy()
83f30e
+    new_env['LD_TRACE_LOADED_OBJECTS'] = '1'
83f30e
+    trace_out = subprocess.run(cmd, stdout=subprocess.PIPE, check=True,
83f30e
+                               universal_newlines=True, env=new_env).stdout
83f30e
+    trace = []
83f30e
+    for line in trace_out.splitlines():
83f30e
+        line = line.strip()
83f30e
+        if is_vdso(line):
83f30e
+            continue
83f30e
+        fields = line.split('=>' if '=>' in line else ' ')
83f30e
+        lib = os.path.basename(fields[0].strip())
83f30e
+        if lib.startswith('ld'):
83f30e
+            lib = 'ld'
83f30e
+        elif lib.startswith('libc'):
83f30e
+            lib = 'libc'
83f30e
+        found = 1 if fields[1].strip() != 'not found' else 0
83f30e
+        trace += ['{} {}'.format(lib, found)]
83f30e
+    trace = sorted(trace)
83f30e
+
83f30e
+    reference = sorted(line.replace('\n','') for line in fref.readlines())
83f30e
+
83f30e
+    ret = 0 if trace == reference else 1
83f30e
+    if ret != 0:
83f30e
+        for i in reference:
83f30e
+            if i not in trace:
83f30e
+                print("Only in {}: {}".format(fref.name, i))
83f30e
+        for i in trace:
83f30e
+            if i not in reference:
83f30e
+                print("Only in trace: {}".format(i))
83f30e
+
83f30e
+    sys.exit(ret)
83f30e
+
83f30e
+
83f30e
+def get_parser():
83f30e
+    parser = argparse.ArgumentParser(description=__doc__)
83f30e
+    parser.add_argument('command',
83f30e
+                        help='comand to run')
83f30e
+    parser.add_argument('reference',
83f30e
+                        help='reference file to compare')
83f30e
+    return parser
83f30e
+
83f30e
+
83f30e
+def main(argv):
83f30e
+    parser = get_parser()
83f30e
+    opts = parser.parse_args(argv)
83f30e
+    with open(opts.reference, 'r') as fref:
83f30e
+        # Remove the initial 'env' command.
83f30e
+        parse_trace(opts.command.split()[1:], fref)
83f30e
+
83f30e
+
83f30e
+if __name__ == '__main__':
83f30e
+    main(sys.argv[1:])