RFC: ld.so: Add DT_FLAGS_2 and DF_2_GNU_IFUNC [BZ #20019]
Commit Message
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
Comments
On 05/25/2018 10:26 PM, H.J. Lu wrote:
> +$(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),$^)
Does this still link against libc.so.6? If not, then this is the bug.
> 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
> +<http://www.gnu.org/licenses/>. */
> +
> +void
> +bar (char *dst, const char *src, unsigned int size)
> +{
> + __builtin_memmove (dst, src, size);
> +}
What happens if you add an IFUNC resolver here? Doesn't the problem
return as before?
Thanks,
Florian
On Fri, May 25, 2018 at 2:52 PM, Florian Weimer <fweimer@redhat.com> wrote:
> On 05/25/2018 10:26 PM, H.J. Lu wrote:
>>
>> +$(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),$^)
>
>
> Does this still link against libc.so.6? If not, then this is the bug.
It is not linked against libc.so.6 and it is done on purpose.
>
>> 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
>> +<http://www.gnu.org/licenses/>. */
>> +
>> +void
>> +bar (char *dst, const char *src, unsigned int size)
>> +{
>> + __builtin_memmove (dst, src, size);
>> +}
>
>
> What happens if you add an IFUNC resolver here? Doesn't the problem return
> as before?
https://sourceware.org/bugzilla/show_bug.cgi?id=23240
On 05/26/2018 12:01 AM, H.J. Lu wrote:
> On Fri, May 25, 2018 at 2:52 PM, Florian Weimer <fweimer@redhat.com> wrote:
>> On 05/25/2018 10:26 PM, H.J. Lu wrote:
>>>
>>> +$(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),$^)
>>
>>
>> Does this still link against libc.so.6? If not, then this is the bug.
>
> It is not linked against libc.so.6 and it is done on purpose.
But this makes the test case invalid.
Florian
On 05/25/2018 06:01 PM, H.J. Lu wrote:
> On Fri, May 25, 2018 at 2:52 PM, Florian Weimer <fweimer@redhat.com> wrote:
>> On 05/25/2018 10:26 PM, H.J. Lu wrote:
>>>
>>> +$(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),$^)
>>
>>
>> Does this still link against libc.so.6? If not, then this is the bug.
>
> It is not linked against libc.so.6 and it is done on purpose.
That's OK, but then you *cannot* call libc.so.6 functions, since you have
no dependency against the library, symbol versioning won't work correctly,
and the dynamic loader will not order initialization correctly (as you note).
Cheers,
Carlos.
On Mon, May 28, 2018 at 9:56 AM, Carlos O'Donell <carlos@redhat.com> wrote:
> On 05/25/2018 06:01 PM, H.J. Lu wrote:
>> On Fri, May 25, 2018 at 2:52 PM, Florian Weimer <fweimer@redhat.com> wrote:
>>> On 05/25/2018 10:26 PM, H.J. Lu wrote:
>>>>
>>>> +$(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),$^)
>>>
>>>
>>> Does this still link against libc.so.6? If not, then this is the bug.
>>
>> It is not linked against libc.so.6 and it is done on purpose.
>
> That's OK, but then you *cannot* call libc.so.6 functions, since you have
> no dependency against the library, symbol versioning won't work correctly,
> and the dynamic loader will not order initialization correctly (as you note).
>
I withdrew this.
@@ -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),$^)
@@ -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
@@ -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;
@@ -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);
new file mode 100644
@@ -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
+ <http://www.gnu.org/licenses/>. */
+
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+
+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 <support/test-driver.c>
new file mode 100644
@@ -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
+ <http://www.gnu.org/licenses/>. */
+
+void
+bar (char *dst, const char *src, unsigned int size)
+{
+ __builtin_memmove (dst, src, size);
+}
new file mode 100644
@@ -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
+ <http://www.gnu.org/licenses/>. */
+
+extern void bar (char *dst, const char *src, unsigned int);
+
+void
+foo (char *dst, const char *src, unsigned int size)
+{
+ bar (dst, src, size);
+}
@@ -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'. */