From patchwork Fri Sep 6 15:59:52 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 34412 Received: (qmail 88207 invoked by alias); 6 Sep 2019 15:59:59 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 88199 invoked by uid 89); 6 Sep 2019 15:59:58 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-18.5 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, SPF_HELO_PASS, SPF_PASS autolearn=ham version=3.3.1 spammy=Request, auditors, Major X-HELO: mx1.redhat.com From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH] ld.so: Enable preloading of new symbol versions [BZ #24974] Date: Fri, 06 Sep 2019 17:59:52 +0200 Message-ID: <877e6le7pj.fsf@oldenburg2.str.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.2 (gnu/linux) MIME-Version: 1.0 This commit adds the --no-version-coverage-check option to the dynamic loader, and the LD_NO_VERSION_COVERAGE_CHECK environment variable. The new _dl_no_version_coverage_check field in struct rtld_global_ro lands in previously-unused padding (on x86-64). 2019-09-06 Florian Weimer [BZ #24974] * elf/rtld.c (dl_main): Add --no-version-coverage-check option. (process_envvars): Handle LD_NO_VERSION_COVERAGE_CHECK. * sysdeps/generic/ldsodefs.h (struct rtld_global_ro): Add _dl_no_version_coverage_check field. * elf/dl-version.c (match_symbol): Check it before reporting an error. * elf/Makefile [$(build-shared)] (tests): Add tst-no-version-coverage-lazy, tst-no-version-coverage-now. (modules-names): Add tst-no-version-coverage-linkmod, tst-no-version-coverage-runmod, tst-no-version-coverage-preloadmod. (LDFLAGS-tst-no-version-coverage-linkmod.so): Set version map and soname. (LDFLAGS-tst-no-version-coverage-runmod.so): Likewise. (LDFLAGS-tst-no-version-coverage-preloadmod.so): Likewise. (LDFLAGS-tst-no-version-coverage-lazy): Enable lazy binding. (tst-no-version-coverage-lazy): Link with tst-no-version-coverage-linkmod.so. (tst-no-version-coverage-lazy-ENV): Disable symbol version coverage check. (tst-no-version-coverage-lazy.out): Depend on tst-no-version-coverage-runmod.so. (LDFLAGS-tst-no-version-coverage-now): Disable lazy binding. (tst-no-version-coverage-now): Link with tst-no-version-coverage-linkmod.so. (tst-no-version-coverage-now-ENV): Preload tst-no-version-coverage-preloadmod.so. Disable symbol version coverage check. (tst-no-version-coverage-now.out): Depend on tst-no-version-coverage-preloadmod.so, tst-no-version-coverage-runmod.so. * elf/tst-no-version-coverage-lazy.c: New file. * elf/tst-no-version-coverage-linkmod.c:: Likewise. * elf/tst-no-version-coverage-linkmod.map: Likewise. * elf/tst-no-version-coverage-now.c: Likewise. * elf/tst-no-version-coverage-preloadmod.c: Likewise. * elf/tst-no-version-coverage-preloadmod.map: Likewise. * elf/tst-no-version-coverage-runmod.c: Likewise. * elf/tst-no-version-coverage-runmod.map: Likewise. diff --git a/NEWS b/NEWS index a64b89986a..4038bb3d5c 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,14 @@ Major new features: 18661-1:2014 and TS 18661-3:2015 as amended by the resolution of Clarification Request 13 to TS 18661-3. +* A new command line option, --no-version-coverage-check, and a new + environment variable, LD_NO_VERSION_COVERAGE_CHECK=1, have been added to + the dynamic linker. If enabled, it is possible to load objects which do + not provide the full set of version definitions required by other objects. + Instead, it is expected that the missing symbols and their versions are + provided by another shared object, via LD_PRELOAD. In effect, this + permits adding new symbol versions to existing shared objects at run time. + Deprecated and removed features, and other changes affecting compatibility: * The totalorder and totalordermag functions, and the corresponding diff --git a/elf/Makefile b/elf/Makefile index d470e41402..8ed23f6986 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -192,7 +192,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \ tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \ tst-unwind-ctor tst-unwind-main tst-audit13 \ - tst-sonamemove-link tst-sonamemove-dlopen tst-dlopen-aout + tst-sonamemove-link tst-sonamemove-dlopen tst-dlopen-aout \ + tst-no-version-coverage-lazy tst-no-version-coverage-now # reldep9 tests-internal += loadtest unload unload2 circleload1 \ neededtest neededtest2 neededtest3 neededtest4 \ @@ -279,7 +280,10 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \ tst-absolute-zero-lib tst-big-note-lib tst-unwind-ctor-lib \ tst-audit13mod1 tst-sonamemove-linkmod1 \ - tst-sonamemove-runmod1 tst-sonamemove-runmod2 + tst-sonamemove-runmod1 tst-sonamemove-runmod2 \ + tst-no-version-coverage-linkmod \ + tst-no-version-coverage-runmod \ + tst-no-version-coverage-preloadmod # Most modules build with _ISOMAC defined, but those filtered out # depend on internal headers. modules-names-tests = $(filter-out ifuncmod% tst-libc_dlvsym-dso tst-tlsmod%,\ @@ -1432,6 +1436,32 @@ $(objpfx)tst-sonamemove-dlopen.out: \ $(objpfx)tst-sonamemove-runmod1.so \ $(objpfx)tst-sonamemove-runmod2.so +LDFLAGS-tst-no-version-coverage-linkmod.so = \ + -Wl,--version-script=tst-no-version-coverage-linkmod.map \ + -Wl,-soname,tst-no-version-coverage-runmod.so +LDFLAGS-tst-no-version-coverage-runmod.so = \ + -Wl,--version-script=tst-no-version-coverage-runmod.map \ + -Wl,-soname,tst-no-version-coverage-runmod.so +LDFLAGS-tst-no-version-coverage-preloadmod.so = \ + -Wl,--version-script=tst-no-version-coverage-preloadmod.map \ + -Wl,-soname,tst-no-version-coverage-preloadmod.so +LDFLAGS-tst-no-version-coverage-lazy = -Wl,-z,lazy +$(objpfx)tst-no-version-coverage-lazy: \ + $(objpfx)tst-no-version-coverage-linkmod.so +tst-no-version-coverage-lazy-ENV = \ + LD_NO_VERSION_COVERAGE_CHECK=1 +$(objpfx)tst-no-version-coverage-lazy.out: \ + $(objpfx)tst-no-version-coverage-runmod.so +LDFLAGS-tst-no-version-coverage-now = -Wl,-z,now +$(objpfx)tst-no-version-coverage-now: \ + $(objpfx)tst-no-version-coverage-linkmod.so +tst-no-version-coverage-now-ENV = \ + LD_NO_VERSION_COVERAGE_CHECK=1 \ + LD_PRELOAD=tst-no-version-coverage-preloadmod.so +$(objpfx)tst-no-version-coverage-now.out: \ + $(objpfx)tst-no-version-coverage-preloadmod.so \ + $(objpfx)tst-no-version-coverage-runmod.so + # Override -z defs, so that we can reference an undefined symbol. # Force lazy binding for the same reason. LDFLAGS-tst-latepthreadmod.so = \ diff --git a/elf/dl-version.c b/elf/dl-version.c index 53c0af3d15..e54b2cd7ff 100644 --- a/elf/dl-version.c +++ b/elf/dl-version.c @@ -125,6 +125,11 @@ checking for version `%s' in file %s [%lu] required by file %s [%lu]\n", def = (ElfW(Verdef) *) ((char *) def + def->vd_next); } + /* If the dynamic linker was told not check for version coverage, do + not report this error. */ + if (GLRO (dl_no_version_coverage_check)) + return 0; + /* Symbol not found. If it was a weak reference it is not fatal. */ if (__glibc_likely (weak)) { diff --git a/elf/rtld.c b/elf/rtld.c index c9490ff694..aed1b0d98c 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -1197,6 +1197,12 @@ dl_main (const ElfW(Phdr) *phdr, _dl_argc -= 2; _dl_argv += 2; } + else if (strcmp (_dl_argv[1], "--no-version-coverage-check") == 0) + { + GLRO (dl_no_version_coverage_check) = true; + --_dl_argc; + ++_dl_argv; + } else break; @@ -1226,7 +1232,11 @@ of this helper program; chances are you did not intend to run this program.\n\ --inhibit-rpath LIST ignore RUNPATH and RPATH information in object names\n\ in LIST\n\ --audit LIST use objects named in LIST as auditors\n\ - --preload LIST preload objects named in LIST\n"); + --preload LIST preload objects named in LIST\n\ + --no-version-coverage-check\n\ + allow loading of objects which reference symbol\n\ + versions which are missing from other objects\n\ +"); ++_dl_skip_args; --_dl_argc; @@ -2677,6 +2687,12 @@ process_envvars (enum mode *modep) mode = trace; break; + case 25: + if (!__libc_enable_secure + && memcmp (envline, "NO_VERSION_COVERAGE_CHECK", 25) == 0) + GLRO (dl_no_version_coverage_check) = envline[26] == '1'; + break; + /* We might have some extra environment variable to handle. This is tricky due to the pre-processing of the length of the name in the switch statement here. The code here assumes that added diff --git a/elf/tst-no-version-coverage-lazy.c b/elf/tst-no-version-coverage-lazy.c new file mode 100644 index 0000000000..71de1ac935 --- /dev/null +++ b/elf/tst-no-version-coverage-lazy.c @@ -0,0 +1,44 @@ +/* Test for the no-version-coverage mode in ld.so, with lazy binding. + Copyright (C) 2019 Free Software Foundation, Inc. + 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 + . */ + +/* Defined in tst-no-version-coverage-linkmod.so and in + tst-no-version-coverage-runmod.so. */ +void version_1_function (void); + +/* Only defined in tst-no-version-coverage-linkmod.so. */ +void version_2_function (void); + +__attribute__ ((noinline, noclone, weak)) +void +compiler_barrier (int call_2) +{ + version_1_function (); + if (call_2) + /* This function is not defined at run time, but the test should + still pass due to lazy binding. */ + version_2_function (); +} + +static int +do_test (void) +{ + compiler_barrier (0); + return 0; +} + +#include diff --git a/elf/tst-no-version-coverage-linkmod.c b/elf/tst-no-version-coverage-linkmod.c new file mode 100644 index 0000000000..fb6c1a3a98 --- /dev/null +++ b/elf/tst-no-version-coverage-linkmod.c @@ -0,0 +1,29 @@ +/* Link interface for exercising the no-version-coverage mode in ld.so. + Copyright (C) 2019 Free Software Foundation, Inc. + 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 + . */ + +/* This function is still present at run time. */ +void +version_1_function (void) +{ +} + +/* This function will be removed at run time. */ +void +version_2_function (void) +{ +} diff --git a/elf/tst-no-version-coverage-linkmod.map b/elf/tst-no-version-coverage-linkmod.map new file mode 100644 index 0000000000..c1126d8940 --- /dev/null +++ b/elf/tst-no-version-coverage-linkmod.map @@ -0,0 +1,7 @@ +VERSION_1 { + version_1_function; +}; + +VERSION_2 { + version_2_function; +} VERSION_1; diff --git a/elf/tst-no-version-coverage-now.c b/elf/tst-no-version-coverage-now.c new file mode 100644 index 0000000000..3133c46d3b --- /dev/null +++ b/elf/tst-no-version-coverage-now.c @@ -0,0 +1,35 @@ +/* Test for the no-version-coverage mode in ld.so, with BIND_NOW. + Copyright (C) 2019 Free Software Foundation, Inc. + 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 + . */ + +/* Defined in tst-no-version-coverage-linkmod.so and in + tst-no-version-coverage-runmod.so. */ +void version_1_function (void); + +/* Defined in tst-no-version-coverage-linkmod.so and in + tst-no-version-coverage-preloadmod.so. */ +void version_2_function (void); + +static int +do_test (void) +{ + version_1_function (); + version_2_function (); + return 0; +} + +#include diff --git a/elf/tst-no-version-coverage-preloadmod.c b/elf/tst-no-version-coverage-preloadmod.c new file mode 100644 index 0000000000..4bff8c496c --- /dev/null +++ b/elf/tst-no-version-coverage-preloadmod.c @@ -0,0 +1,23 @@ +/* Preloaded DSO for exercising the no-version-coverage mode in ld.so. + Copyright (C) 2019 Free Software Foundation, Inc. + 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 + . */ + +/* This function supplies the required function. */ +void +version_2_function (void) +{ +} diff --git a/elf/tst-no-version-coverage-preloadmod.map b/elf/tst-no-version-coverage-preloadmod.map new file mode 100644 index 0000000000..7613b6d7de --- /dev/null +++ b/elf/tst-no-version-coverage-preloadmod.map @@ -0,0 +1,3 @@ +VERSION_2 { + version_2_function; +}; diff --git a/elf/tst-no-version-coverage-runmod.c b/elf/tst-no-version-coverage-runmod.c new file mode 100644 index 0000000000..39d9d063e3 --- /dev/null +++ b/elf/tst-no-version-coverage-runmod.c @@ -0,0 +1,23 @@ +/* Run-time DSO for exercising the no-version-coverage mode in ld.so. + Copyright (C) 2019 Free Software Foundation, Inc. + 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 + . */ + +/* This function is still present at run time. */ +void +version_1_function (void) +{ +} diff --git a/elf/tst-no-version-coverage-runmod.map b/elf/tst-no-version-coverage-runmod.map new file mode 100644 index 0000000000..c5d9e29554 --- /dev/null +++ b/elf/tst-no-version-coverage-runmod.map @@ -0,0 +1,3 @@ +VERSION_1 { + version_1_function; +}; diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 1e193b05b0..4c75e207e9 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -629,6 +629,13 @@ struct rtld_global_ro /* List of auditing interfaces. */ struct audit_ifaces *_dl_audit; unsigned int _dl_naudit; +#endif /* SHARED */ + + /* If true, allow references to versioned symbols which are not + defined. */ + bool _dl_no_version_coverage_check; + +#ifdef SHARED }; # define __rtld_global_attribute__ # if IS_IN (rtld) @@ -645,7 +652,7 @@ extern const struct rtld_global_ro _rtld_global_ro attribute_relro __rtld_global_attribute__; # endif # undef __rtld_global_attribute__ -#endif +#endif /* SHARED */ #undef EXTERN #ifndef SHARED