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