From patchwork Fri May 25 20:26:34 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Lu, Hongjiu" X-Patchwork-Id: 27510 Received: (qmail 87917 invoked by alias); 25 May 2018 20:26:39 -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 87419 invoked by uid 89); 25 May 2018 20:26:38 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-24.8 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, KAM_SHORT, NO_DNS_FOR_FROM autolearn=ham version=3.3.2 spammy=RPATH, sk:conside, RUNPATH, GNU_IFUNC X-HELO: mga05.intel.com X-Amp-Result: UNKNOWN X-Amp-Original-Verdict: FILE UNKNOWN X-Amp-File-Uploaded: False X-ExtLoop1: 1 Date: Fri, 25 May 2018 13:26:34 -0700 From: "H.J. Lu" To: GNU C Library Subject: RFC: ld.so: Add DT_FLAGS_2 and DF_2_GNU_IFUNC [BZ #20019] Message-ID: <20180525202634.GA23760@intel.com> Reply-To: "H.J. Lu" MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.9.5 (2018-04-13) As shown in https://sourceware.org/bugzilla/show_bug.cgi?id=20019 it is possible to create a shared object which references an IFUNC function defined in another shared object. With non-lazy binding at run-time, usage of such a shared object can lead to segfault. The issue is the shared object, which provides the IFUNC function, isn't in DT_NEEDED and dynamic linker tries to resolve the reference to the IFUNC function defined in the unrelocated shared object with non-lazy binding. Currently, the glibc dynamic linker issues a warning: [hjl@gnu-cfl-1 pr20019]$ ./main-dynamic ./main-dynamic: Relink `./libbar.so' with `/export/build/gnu/glibc/build-x86_64- linux/libc.so.6' for IFUNC symbol `memmove' Segmentation fault [hjl@gnu-cfl-1 pr20019]$ But it doesn't prevent segfault. This can happen more often with LD_PRELOAD of shared objects with IFUNC symbols on executables and shared objects with DT_BIND_NOW. This patch updates ld.so to check the new DT_FLAGS_2 and DF_2_GNU_IFUNC, proposed at https://sourceware.org/ml/binutils/2018-05/msg00264.html which indicates that a shared object has IFUNC symbols and relocate it first. I also renamed l_feature_1 in link_map to l_flags_2 since it is unused. [BZ #20019] * elf/Makefile (tests): Add tst-reloc1. (test-xfail-tst-reloc1): New. (modules-names): Add tst-relocmod1a and tst-relocmod1b. ($(objpfx)tst-reloc1): New. ($(objpfx)tst-relocmod1b.so): Likewise. ($(objpfx)tst-relocmod1a.so): Likewise. * elf/elf.h (DT_FLAGS_2): New macro. (DF_2_GNU_IFUNC) : New. * elf/get-dynamic-info.h (elf_get_dynamic_info): Also get DT_FLAGS_2. * elf/rtld.c (dl_main): Relocate shared objects with IFUNC symbols first. * elf/tst-reloc1.c: New file. * elf/tst-relocmod1a.c: Likewise. * elf/tst-relocmod1b.c: Likewise. * include/link.h (link_map): Rename l_feature_1 to l_flags_2. --- elf/Makefile | 14 ++++++++++++-- elf/elf.h | 6 ++++++ elf/get-dynamic-info.h | 2 ++ elf/rtld.c | 21 +++++++++++++++++++++ elf/tst-reloc1.c | 39 +++++++++++++++++++++++++++++++++++++++ elf/tst-relocmod1a.c | 23 +++++++++++++++++++++++ elf/tst-relocmod1b.c | 25 +++++++++++++++++++++++++ include/link.h | 2 +- 8 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 elf/tst-reloc1.c create mode 100644 elf/tst-relocmod1a.c create mode 100644 elf/tst-relocmod1b.c diff --git a/elf/Makefile b/elf/Makefile index 2dcd2b88e0..7d78e28c9e 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -186,8 +186,9 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ tst-tlsalign tst-tlsalign-extern tst-nodelete-opened \ tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \ tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \ - tst-debug1 tst-main1 tst-absolute-sym tst-big-note + tst-debug1 tst-main1 tst-absolute-sym tst-big-note tst-reloc1 # reldep9 +test-xfail-tst-reloc1 = yes tests-internal += loadtest unload unload2 circleload1 \ neededtest neededtest2 neededtest3 neededtest4 \ tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \ @@ -273,7 +274,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ tst-latepthreadmod $(tst-tls-many-dynamic-modules) \ tst-nodelete-dlclose-dso tst-nodelete-dlclose-plugin \ tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \ - tst-big-note-lib + tst-big-note-lib tst-relocmod1a tst-relocmod1b ifeq (yes,$(have-mtls-dialect-gnu2)) tests += tst-gnu2-tls1 @@ -1454,3 +1455,12 @@ tst-libc_dlvsym-static-ENV = \ $(objpfx)tst-libc_dlvsym-static.out: $(objpfx)tst-libc_dlvsym-dso.so $(objpfx)tst-big-note: $(objpfx)tst-big-note-lib.so + +$(objpfx)tst-reloc1: $(objpfx)tst-relocmod1b.so +$(objpfx)tst-relocmod1b.so: $(objpfx)tst-relocmod1b.os \ + $(objpfx)tst-relocmod1a.so + $(LINK.o) -nostdlib -nostartfiles -shared -o $@ -Wl,-z,now \ + $(filter-out $(shlib-lds),$^) +$(objpfx)tst-relocmod1a.so: $(objpfx)tst-relocmod1a.os + $(LINK.o) -nostdlib -nostartfiles -shared -o $@ -Wl,-z,now \ + $(filter-out $(shlib-lds),$^) diff --git a/elf/elf.h b/elf/elf.h index a5b2811ce0..3e32522532 100644 --- a/elf/elf.h +++ b/elf/elf.h @@ -925,6 +925,8 @@ typedef struct GNU extension. */ #define DT_VERSYM 0x6ffffff0 +#define DT_FLAGS_2 0x6ffffff1 /* State flags, see DF_2_* below. */ + #define DT_RELACOUNT 0x6ffffff9 #define DT_RELCOUNT 0x6ffffffa @@ -984,6 +986,10 @@ typedef struct #define DF_1_STUB 0x04000000 #define DF_1_PIE 0x08000000 +/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_2 + entry in the dynamic section. */ +#define DF_2_GNU_IFUNC 0x00000001 /* Object has GNU_IFUNC symbols */ + /* Flags for the feature selection in DT_FEATURE_1. */ #define DTF_1_PARINIT 0x00000001 #define DTF_1_CONFEXP 0x00000002 diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h index 4b1ea7c407..1cea375c40 100644 --- a/elf/get-dynamic-info.h +++ b/elf/get-dynamic-info.h @@ -178,6 +178,8 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp) if (l->l_flags_1 & DF_1_NOW) info[DT_BIND_NOW] = info[VERSYMIDX (DT_FLAGS_1)]; } + if (info[VERSYMIDX (DT_FLAGS_2)] != NULL) + l->l_flags_2 = info[VERSYMIDX (DT_FLAGS_2)]->d_un.d_val; if (info[DT_RUNPATH] != NULL) /* If both RUNPATH and RPATH are given, the latter is ignored. */ info[DT_RPATH] = NULL; diff --git a/elf/rtld.c b/elf/rtld.c index e7681ebb1f..91fb91ef7f 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -2183,6 +2183,27 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", /* Also allocated with the fake malloc(). */ l->l_free_initfini = 0; + /* Relocate shared object with IFUNC symbols first. */ + if (!l->l_name[0] || !(l->l_flags_2 & DF_2_GNU_IFUNC)) + continue; + + if (l != &GL(dl_rtld_map)) + _dl_relocate_object (l, l->l_scope, GLRO(dl_lazy) ? RTLD_LAZY : 0, + consider_profiling); + + /* Add object to slot information data if necessasy. */ + if (l->l_tls_blocksize != 0 && tls_init_tp_called) + _dl_add_to_slotinfo (l); + } + + i = main_map->l_searchlist.r_nlist; + while (i-- > 0) + { + struct link_map *l = main_map->l_initfini[i]; + + if (l->l_relocated) + continue; + if (l != &GL(dl_rtld_map)) _dl_relocate_object (l, l->l_scope, GLRO(dl_lazy) ? RTLD_LAZY : 0, consider_profiling); diff --git a/elf/tst-reloc1.c b/elf/tst-reloc1.c new file mode 100644 index 0000000000..5867a638f4 --- /dev/null +++ b/elf/tst-reloc1.c @@ -0,0 +1,39 @@ +/* Test for relocation over with IFUNC symbols. + Copyright (C) 2018 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 + . */ + +#include +#include +#include + +extern void foo (char *, const char *, unsigned int); + +static int +do_test (void) +{ + char dst[50]; + const char src[] = + { + "This is a test" + }; + foo (dst, src, sizeof (src)); + if (__builtin_memcmp (dst, src, sizeof (src)) != 0) + __builtin_abort (); + return 0; +} + +#include diff --git a/elf/tst-relocmod1a.c b/elf/tst-relocmod1a.c new file mode 100644 index 0000000000..be9f8832b8 --- /dev/null +++ b/elf/tst-relocmod1a.c @@ -0,0 +1,23 @@ +/* Shared module to test for relocation over with IFUNC symbols. + Copyright (C) 2018 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 + . */ + +void +bar (char *dst, const char *src, unsigned int size) +{ + __builtin_memmove (dst, src, size); +} diff --git a/elf/tst-relocmod1b.c b/elf/tst-relocmod1b.c new file mode 100644 index 0000000000..0424dc8b36 --- /dev/null +++ b/elf/tst-relocmod1b.c @@ -0,0 +1,25 @@ +/* Shared module to test for relocation over with IFUNC symbols. + Copyright (C) 2018 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 + . */ + +extern void bar (char *dst, const char *src, unsigned int); + +void +foo (char *dst, const char *src, unsigned int size) +{ + bar (dst, src, size); +} diff --git a/include/link.h b/include/link.h index 5924594548..6acf4e0493 100644 --- a/include/link.h +++ b/include/link.h @@ -264,8 +264,8 @@ struct link_map unsigned int l_used; /* Various flag words. */ - ElfW(Word) l_feature_1; ElfW(Word) l_flags_1; + ElfW(Word) l_flags_2; ElfW(Word) l_flags; /* Temporarily used in `dl_close'. */