From patchwork Wed Feb 9 13:53:56 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella Netto X-Patchwork-Id: 50952 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0E4F1385841B for ; Wed, 9 Feb 2022 13:54:24 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0E4F1385841B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1644414864; bh=sopVzF54vgYxdWnZHtiPmJR8E1dZts51q3rY07Cz8Lk=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=jHY/B8gJQcyKo1K19P1UtsJymBpgWcA83TrxmBbLoVRYmrRb8UoTNViWzjt2uyQYj Al9qPj4yASHvHlwyyz1cvXbpFz9zZx/HPauY4kuWdN9CXELCBrukzI/OCVm5kOQPSf ZDTuVAbaTPLEuSNFUWK+GLAL4MokRIvhT/6tD0yI= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-oo1-xc31.google.com (mail-oo1-xc31.google.com [IPv6:2607:f8b0:4864:20::c31]) by sourceware.org (Postfix) with ESMTPS id BC7633858D28 for ; Wed, 9 Feb 2022 13:54:01 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org BC7633858D28 Received: by mail-oo1-xc31.google.com with SMTP id o192-20020a4a2cc9000000b00300af40d795so2431600ooo.13 for ; Wed, 09 Feb 2022 05:54:01 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=sopVzF54vgYxdWnZHtiPmJR8E1dZts51q3rY07Cz8Lk=; b=gv1Er+ko6uD79KC4S3Buf/Y55x3Ns12zxu5+Dh1bCGHg/Z7Lv2fJsn5WVmKMfFhcSL lF1M+6py0aJvque7xG6SZ4p9t+VVjUTPn51IiOFzHko9nf/44QVXejK3Yu7Pg5HSH5TR fr1eVMiuoKHk3YCQFwKkWNqJu5YNRBbji43FAhwFq/uCNScwihKh5syJRuhO2oBGgqav ysaolMwdu56K3325lGZTcqMxDaIGTuBl80ZHqOb/NgyXKeDVb1ufK0CVLwFt+8iR0auL 7mCU8KUAs0M0eDaZwHNy+vu5mWSEkZkS+AOHW8Kfri9zVoTxbpHeVOui3to99WIGQOXT B7jQ== X-Gm-Message-State: AOAM532bKjFD0DrPsiZ+dZJtfpicYr100+Aol/Q6EUJZ/jwNi37JeeLi 3xfCrylwX9IfOU3axTIJTOCHCoPR519iAA== X-Google-Smtp-Source: ABdhPJx9LL19uPfXD9ZhAIHHdNhIVWMNswjLc3SMBsiyBb+vtc33N5w52mzQx/Xn7GEpqUl58E+VRA== X-Received: by 2002:a4a:4ec6:: with SMTP id r189mr886272ooa.31.1644414840879; Wed, 09 Feb 2022 05:54:00 -0800 (PST) Received: from birita.. ([2804:431:c7ca:733:620:153c:dcac:6440]) by smtp.gmail.com with ESMTPSA id w7sm1337044oiv.8.2022.02.09.05.53.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 09 Feb 2022 05:54:00 -0800 (PST) To: libc-alpha@sourceware.org Subject: [PATCH v2] elf: Fix DFS sorting algorithm for LD_TRACE_LOADED_OBJECTS with missing libraries (BZ #28868) Date: Wed, 9 Feb 2022 10:53:56 -0300 Message-Id: <20220209135356.248219-1-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.32.0 MIME-Version: 1.0 X-Spam-Status: No, score=-12.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Adhemerval Zanella via Libc-alpha From: Adhemerval Zanella Netto Reply-To: Adhemerval Zanella Errors-To: libc-alpha-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libc-alpha" The _dl_map_object_deps ignores l_faked objects (set if the underlying file can't be opened by _dl_map_object): 490 for (nlist = 0, runp = known; runp; runp = runp->next) 491 { 492 if (__builtin_expect (trace_mode, 0) && runp->map->l_faked) 493 /* This can happen when we trace the loading. */ 494 --map->l_searchlist.r_nlist; 495 else 496 { 497 if (runp->map == map) 498 map_index = nlist; 499 map->l_searchlist.r_list[nlist++] = runp->map; 500 } 501 502 /* Now clear all the mark bits we set in the objects on the search list 503 to avoid duplicates, so the next call starts fresh. */ 504 runp->map->l_reserved = 0; 505 } If there any missing libraries being processed, they will not be considered on final nlist size passed on _dl_sort_maps later in the function. And it is then used on _dl_sort_maps_dfs on the stack allocated working maps: 222 /* Array to hold RPO sorting results, before we copy back to maps[]. */ 223 struct link_map *rpo[nmaps]; 224 225 /* The 'head' position during each DFS iteration. Note that we start at 226 one past the last element due to first-decrement-then-store (see the 227 bottom of above dfs_traversal() routine). */ 228 struct link_map **rpo_head = &rpo[nmaps]; However while transversing the 'l_initfini' on dfs_traversal it will still considere the l_faked maps and thus update rpo more times than the allocated working 'rpo', overflowing the stack object. As suggested in bugzilla, one option would be to avoid sorting the maps for trace mode. However I think ignoring l_faked object does make sense (there is one less constraint to call the sorting function), it allows a slight less stack usage for trace, and it is slight simpler solution. The tests does trigger the stack overflow, however I tried to make it more generic to check different scenarios or missing objects. Checked on x86_64-linux-gnu --- v2: Added comments and fixed the default tst-trace1 --library-path. --- elf/Makefile | 61 +++++++++++++++++++++++++++++++++ elf/dl-deps.c | 2 ++ elf/dl-sort-maps.c | 4 ++- elf/libtracemod1.c | 1 + elf/libtracemod2.c | 1 + elf/libtracemod3.c | 1 + elf/libtracemod4.c | 1 + elf/libtracemod5.c | 1 + elf/libtracemod6.c | 1 + elf/tst-trace1.exp | 4 +++ elf/tst-trace2.exp | 6 ++++ elf/tst-trace3.exp | 6 ++++ elf/tst-trace4.exp | 6 ++++ elf/tst-trace5.exp | 6 ++++ scripts/tst-ld-trace.py | 75 +++++++++++++++++++++++++++++++++++++++++ 15 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 elf/libtracemod1.c create mode 100644 elf/libtracemod2.c create mode 100644 elf/libtracemod3.c create mode 100644 elf/libtracemod4.c create mode 100644 elf/libtracemod5.c create mode 100644 elf/libtracemod6.c create mode 100644 elf/tst-trace1.exp create mode 100644 elf/tst-trace2.exp create mode 100644 elf/tst-trace3.exp create mode 100644 elf/tst-trace4.exp create mode 100644 elf/tst-trace5.exp create mode 100755 scripts/tst-ld-trace.py diff --git a/elf/Makefile b/elf/Makefile index b2bd03a9f6..07b27010f6 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -819,6 +819,11 @@ modules-names = \ tst-tlsmod8 \ tst-tlsmod9 \ tst-unique1mod1 \ + libtracemod1 \ + libtracemod2 \ + libtracemod3 \ + libtracemod4 \ + libtracemod5 \ tst-unique1mod2 \ tst-unique2mod1 \ tst-unique2mod2 \ @@ -1072,6 +1077,8 @@ tests-special += \ $(objpfx)tst-initorder2-cmp.out \ $(objpfx)tst-unused-dep-cmp.out \ $(objpfx)tst-unused-dep.out \ + $(objpfx)tst-trace1.out \ + $(objpfx)tst-trace2.out \ # tests-special endif @@ -2733,3 +2740,57 @@ $(objpfx)tst-p_align3: $(objpfx)tst-p_alignmod3.so $(objpfx)tst-p_align3.out: tst-p_align3.sh $(objpfx)tst-p_align3 $(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \ $(evaluate-test) + + +libtracemod-suffixes = 5 4 3 2 1 +define libtracemod +$(objpfx)libtracemod$(1).stamp: $(objpfx)libtracemod$(1).so + touch $(objpfx)libtracemod$(1).stamp +endef +$(foreach s,$(libtracemod-suffixes), $(eval $(call libtracemod,$(s)))) + +# Move the library to a folder so it can be selected by --library-path +define libtracemod-mv + test -d $(objpfx)libtracemod$(1) || mkdir $(objpfx)libtracemod$(1) + test -f $(objpfx)libtracemod$(1).so \ + && mv $(objpfx)libtracemod$(1).so $(objpfx)libtracemod$(1) +endef +libtracemod-mv: $(objpfx)libtracemod1.stamp + $(call libtracemod-mv,2) + $(call libtracemod-mv,3) + $(call libtracemod-mv,4) + $(call libtracemod-mv,5) + +LDFLAGS-libtracemod1.so = -Wl,--no-as-needed \ + -L$(objpfx) -ltracemod2 -ltracemod3 +LDFLAGS-libtracemod2.so = -Wl,--no-as-needed \ + -L$(objpfx) -ltracemod4 -ltracemod5 +$(objpfx)libtracemod2.so: $(objpfx)libtracemod4.stamp \ + $(objpfx)libtracemod5.stamp +$(objpfx)libtracemod1.so: $(objpfx)libtracemod2.stamp \ + $(objpfx)libtracemod3.stamp + +define tst-trace-skeleton +$(objpfx)tst-trace$(1).out: $(..)scripts/tst-ld-trace.py \ + $(objpfx)libtracemod1.so \ + libtracemod-mv \ + tst-trace$(1).exp + ( $(test-wrapper-env) \ + LD_TRACE_LOADED_OBJECTS=1 \ + $(elf-objpfx)$(rtld-installed-name) \ + --library-path $(objpfx)..:$(strip $(2)) \ + $(objpfx)libtracemod1.so > $$@T \ + && $(PYTHON) $(..)scripts/tst-ld-trace.py $$@T tst-trace$(1).exp \ + ) > $$@; $$(evaluate-test) +endef + +$(eval $(call tst-trace-skeleton,1,)) +$(eval $(call tst-trace-skeleton,2,\ + $(objpfx)libtracemod2)) +$(eval $(call tst-trace-skeleton,3,\ + $(objpfx)libtracemod2:$(objpfx)libtracemod3)) +$(eval $(call tst-trace-skeleton,4,\ + $(objpfx)libtracemod2:$(objpfx)libtracemod3:$(objpfx)libtracemod4)) +$(eval $(call tst-trace-skeleton,5,\ + $(objpfx)libtracemod2:$(objpfx)libtracemod3:$(objpfx)libtracemod4:\ + $(objpfx)libtracemod5)) diff --git a/elf/dl-deps.c b/elf/dl-deps.c index c8bab5cad5..8b39383359 100644 --- a/elf/dl-deps.c +++ b/elf/dl-deps.c @@ -489,6 +489,8 @@ _dl_map_object_deps (struct link_map *map, for (nlist = 0, runp = known; runp; runp = runp->next) { + /* _dl_sort_maps ignores l_faked object, so it is save to not considere + them for nlist. */ if (__builtin_expect (trace_mode, 0) && runp->map->l_faked) /* This can happen when we trace the loading. */ --map->l_searchlist.r_nlist; diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c index 9e9d53ec47..2ed62da7dd 100644 --- a/elf/dl-sort-maps.c +++ b/elf/dl-sort-maps.c @@ -140,7 +140,9 @@ static void dfs_traversal (struct link_map ***rpo, struct link_map *map, bool *do_reldeps) { - if (map->l_visited) + /* _dl_map_object_deps filter l_faked objects when calculating the + number of maps before calling _dl_sort_maps, ignore them as well. */ + if (map->l_visited || map->l_faked) return; map->l_visited = 1; diff --git a/elf/libtracemod1.c b/elf/libtracemod1.c new file mode 100644 index 0000000000..7c89c9a5a4 --- /dev/null +++ b/elf/libtracemod1.c @@ -0,0 +1 @@ +/* Empty */ diff --git a/elf/libtracemod2.c b/elf/libtracemod2.c new file mode 100644 index 0000000000..7c89c9a5a4 --- /dev/null +++ b/elf/libtracemod2.c @@ -0,0 +1 @@ +/* Empty */ diff --git a/elf/libtracemod3.c b/elf/libtracemod3.c new file mode 100644 index 0000000000..7c89c9a5a4 --- /dev/null +++ b/elf/libtracemod3.c @@ -0,0 +1 @@ +/* Empty */ diff --git a/elf/libtracemod4.c b/elf/libtracemod4.c new file mode 100644 index 0000000000..7c89c9a5a4 --- /dev/null +++ b/elf/libtracemod4.c @@ -0,0 +1 @@ +/* Empty */ diff --git a/elf/libtracemod5.c b/elf/libtracemod5.c new file mode 100644 index 0000000000..7c89c9a5a4 --- /dev/null +++ b/elf/libtracemod5.c @@ -0,0 +1 @@ +/* Empty */ diff --git a/elf/libtracemod6.c b/elf/libtracemod6.c new file mode 100644 index 0000000000..7c89c9a5a4 --- /dev/null +++ b/elf/libtracemod6.c @@ -0,0 +1 @@ +/* Empty */ diff --git a/elf/tst-trace1.exp b/elf/tst-trace1.exp new file mode 100644 index 0000000000..4a6f5211a6 --- /dev/null +++ b/elf/tst-trace1.exp @@ -0,0 +1,4 @@ +ld 1 +libc 1 +libtracemod2.so 0 +libtracemod3.so 0 diff --git a/elf/tst-trace2.exp b/elf/tst-trace2.exp new file mode 100644 index 0000000000..e13506e2eb --- /dev/null +++ b/elf/tst-trace2.exp @@ -0,0 +1,6 @@ +ld 1 +libc 1 +libtracemod2.so 1 +libtracemod3.so 0 +libtracemod4.so 0 +libtracemod5.so 0 diff --git a/elf/tst-trace3.exp b/elf/tst-trace3.exp new file mode 100644 index 0000000000..e574549d12 --- /dev/null +++ b/elf/tst-trace3.exp @@ -0,0 +1,6 @@ +ld 1 +libc 1 +libtracemod2.so 1 +libtracemod3.so 1 +libtracemod4.so 0 +libtracemod5.so 0 diff --git a/elf/tst-trace4.exp b/elf/tst-trace4.exp new file mode 100644 index 0000000000..31ca97b35b --- /dev/null +++ b/elf/tst-trace4.exp @@ -0,0 +1,6 @@ +ld 1 +libc 1 +libtracemod2.so 1 +libtracemod3.so 1 +libtracemod4.so 1 +libtracemod5.so 0 diff --git a/elf/tst-trace5.exp b/elf/tst-trace5.exp new file mode 100644 index 0000000000..5d7d953726 --- /dev/null +++ b/elf/tst-trace5.exp @@ -0,0 +1,6 @@ +ld 1 +libc 1 +libtracemod2.so 1 +libtracemod3.so 1 +libtracemod4.so 1 +libtracemod5.so 1 diff --git a/scripts/tst-ld-trace.py b/scripts/tst-ld-trace.py new file mode 100755 index 0000000000..b45f406afb --- /dev/null +++ b/scripts/tst-ld-trace.py @@ -0,0 +1,75 @@ +#!/usr/bin/python3 +# Dump the output of LD_TRACE_LOADED_OBJECTS in architecture neutral format. +# Copyright (C) 2022 Free Software Foundation, Inc. +# Copyright The GNU Toolchain Authors. +# This file is part of the GNU C Library. +# +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# . + +import argparse +import os +import sys + + +def is_vdso(lib): + return lib.startswith('linux-gate') or lib.startswith ('linux-vdso') + + +def parse_trace(fin, fref): + trace = [] + for line in fin: + line = line.strip() + if is_vdso(line): + continue + fields = line.split('=>' if '=>' in line else ' ') + lib = os.path.basename(fields[0].strip()) + if lib.startswith('ld'): + lib = 'ld' + elif lib.startswith('libc'): + lib = 'libc' + found = 1 if fields[1].strip() != 'not found' else 0 + trace += ['{} {}'.format(lib, found)] + + reference = sorted(line.replace('\n','') for line in fref.readlines()) + + ret = 0 if sorted(trace) == reference else 1 + if ret != 0: + for i in reference: + if i not in trace: + print("Only in {}: {}".format(fref.name, i)) + for i in trace: + if i not in reference: + print("Only in {}: {}".format(fin.name, i)) + sys.exit (ret) + + +def get_parser(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('input', + help='LD_TRACE_LOADED_OBJECTS input') + parser.add_argument('reference', + help='reference file to compare') + return parser + + +def main(argv): + parser = get_parser() + opts = parser.parse_args(argv) + with open(opts.input, 'r') as fin, open(opts.reference, 'r') as fref: + parse_trace(fin, fref) + + +if __name__ == '__main__': + main(sys.argv[1:])