From 0f93f4f0706f2e0f3e4b562907f4701216276808 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Thu, 12 Jan 2017 15:27:05 -0800 Subject: [PATCH] Add an IFUNC testcase for [BZ #20019] When the IFUNC relocation is performed before the providing shared library is relocated, the returned function address will be 0 and program will segfault when the function is called. Add a testcase to verify that ld.so issues a diagnostic. [BZ #20019] * elf/Makefile (tests): Add tst-ifunc1. (tests-special): Add $(objpfx)tst-ifunc1.out. (modules-names): Add tst-ifuncmod1a, tst-ifuncmod1b, tst-ifuncmod1c and tst-ifuncmod1d. ($(objpfx)tst-ifunc1.out): New rule. ($(objpfx)tst-ifunc1): New dependency. ($(objpfx)tst-ifuncmod1a.so): LIkewise. ($(objpfx)tst-ifuncmod1b.so): LIkewise. ($(objpfx)tst-ifuncmod1c.so): LIkewise. (LDFLAGS-tst-ifuncmod1b.so): New. * elf/tst-ifunc1.c: New file. * elf/tst-ifunc1.sh: LIkewise. * elf/tst-ifuncmod1a.c: LIkewise. * elf/tst-ifuncmod1b.c: LIkewise. * elf/tst-ifuncmod1c.c: LIkewise. * elf/tst-ifuncmod1d.c: LIkewise. * sysdeps/generic/ldsodefs.h (dl_check_ifunc_resolver): New function. * sysdeps/i386/dl-machine.h (elf_machine_rel): Use it. (elf_machine_rela): Likewise. * sysdeps/x86_64/dl-machine.h (elf_machine_rela): Likewise. --- elf/Makefile | 20 ++++++++++++++++++-- elf/tst-ifunc1.c | 26 ++++++++++++++++++++++++++ elf/tst-ifunc1.sh | 43 +++++++++++++++++++++++++++++++++++++++++++ elf/tst-ifuncmod1a.c | 25 +++++++++++++++++++++++++ elf/tst-ifuncmod1b.c | 25 +++++++++++++++++++++++++ elf/tst-ifuncmod1c.c | 27 +++++++++++++++++++++++++++ elf/tst-ifuncmod1d.c | 24 ++++++++++++++++++++++++ sysdeps/generic/ldsodefs.h | 26 ++++++++++++++++++++++++++ sysdeps/i386/dl-machine.h | 20 +++++++------------- sysdeps/x86_64/dl-machine.h | 13 +------------ 10 files changed, 222 insertions(+), 27 deletions(-) create mode 100644 elf/tst-ifunc1.c create mode 100755 elf/tst-ifunc1.sh create mode 100644 elf/tst-ifuncmod1a.c create mode 100644 elf/tst-ifuncmod1b.c create mode 100644 elf/tst-ifuncmod1c.c create mode 100644 elf/tst-ifuncmod1d.c diff --git a/elf/Makefile b/elf/Makefile index c7a2969..df2728f 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -307,7 +307,8 @@ tests += ifuncmain1 ifuncmain1pic ifuncmain1vis ifuncmain1vispic \ ifuncmain1staticpic \ ifuncmain2 ifuncmain2pic ifuncmain3 ifuncmain4 \ ifuncmain5 ifuncmain5pic ifuncmain5staticpic \ - ifuncmain7 ifuncmain7pic + ifuncmain7 ifuncmain7pic tst-ifunc1 +tests-special += $(objpfx)tst-ifunc1.out ifunc-test-modules = ifuncdep1 ifuncdep1pic ifuncdep2 ifuncdep2pic \ ifuncdep5 ifuncdep5pic extra-test-objs += $(ifunc-test-modules:=.o) @@ -318,7 +319,9 @@ ifunc-pie-tests = ifuncmain1pie ifuncmain1vispie ifuncmain1staticpie \ tests += $(ifunc-pie-tests) tests-pie += $(ifunc-pie-tests) endif -modules-names += ifuncmod1 ifuncmod3 ifuncmod5 ifuncmod6 +modules-names += ifuncmod1 ifuncmod3 ifuncmod5 ifuncmod6 \ + tst-ifuncmod1a tst-ifuncmod1b tst-ifuncmod1c \ + tst-ifuncmod1d endif endif @@ -1026,6 +1029,19 @@ CFLAGS-tst-pie2.c += $(pie-ccflag) $(objpfx)tst-piemod1.so: $(libsupport) $(objpfx)tst-pie1: $(objpfx)tst-piemod1.so +$(objpfx)tst-ifunc1.out: tst-ifunc1.sh $(objpfx)tst-ifunc1 + $(BASH) $< $(objpfx) '$(test-via-rtld-prefix)' \ + '$(test-wrapper-env)' '$(run-program-env)'; \ + $(evaluate-test) + +$(objpfx)tst-ifunc1: $(objpfx)tst-ifuncmod1a.so \ + $(objpfx)tst-ifuncmod1c.so \ + $(objpfx)tst-ifuncmod1d.so +$(objpfx)tst-ifuncmod1a.so: $(objpfx)tst-ifuncmod1b.so +$(objpfx)tst-ifuncmod1b.so: $(objpfx)tst-ifuncmod1d.so +$(objpfx)tst-ifuncmod1c.so: $(objpfx)tst-ifuncmod1d.so +LDFLAGS-tst-ifuncmod1b.so = -Wl,-z,now + ifeq (yes,$(build-shared)) all-built-dso := $(common-objpfx)elf/ld.so $(common-objpfx)libc.so \ $(filter-out $(common-objpfx)linkobj/libc.so, \ diff --git a/elf/tst-ifunc1.c b/elf/tst-ifunc1.c new file mode 100644 index 0000000..e44dbc3 --- /dev/null +++ b/elf/tst-ifunc1.c @@ -0,0 +1,26 @@ +/* Test case for unsafe IFUNC resolver. + Copyright (C) 2017 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 foo (void); + +int +main (void) +{ + foo (); + return 0; +} diff --git a/elf/tst-ifunc1.sh b/elf/tst-ifunc1.sh new file mode 100755 index 0000000..8ee3f72 --- /dev/null +++ b/elf/tst-ifunc1.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# A Test case for unsafe IFUNC resolver. +# Copyright (C) 2017 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 +# . + +set -e + +objpfx=$1; shift +test_via_rtld_prefix=$1; shift +test_wrapper_env=$1; shift +run_program_env=$1; shift +logfile=${objpfx}tst-ifunc1.out + +> $logfile +fail=0 + +${test_wrapper_env} \ +${run_program_env} \ +${objpfx}tst-ifunc1 2>&1 || fail=1 + +if test $fail = 1; then + # If it fails to run, check for the expected error from ld.so. + fail=0 + ${test_wrapper_env} \ + ${run_program_env} \ + ${objpfx}tst-ifunc1 2>&1 | grep Relink >> $logfile || fail=1 +fi + +exit $fail diff --git a/elf/tst-ifuncmod1a.c b/elf/tst-ifuncmod1a.c new file mode 100644 index 0000000..5e2183c --- /dev/null +++ b/elf/tst-ifuncmod1a.c @@ -0,0 +1,25 @@ +/* Test case for unsafe IFUNC resolver. + Copyright (C) 2017 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 (void); + +void +foo (void) +{ + bar (); +} diff --git a/elf/tst-ifuncmod1b.c b/elf/tst-ifuncmod1b.c new file mode 100644 index 0000000..0c6fe10 --- /dev/null +++ b/elf/tst-ifuncmod1b.c @@ -0,0 +1,25 @@ +/* Test case for unsafe IFUNC resolver. + Copyright (C) 2017 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 xxx (void); + +void +bar (void) +{ + xxx (); +} diff --git a/elf/tst-ifuncmod1c.c b/elf/tst-ifuncmod1c.c new file mode 100644 index 0000000..81cd31c --- /dev/null +++ b/elf/tst-ifuncmod1c.c @@ -0,0 +1,27 @@ +/* Test case for unsafe IFUNC resolver. + Copyright (C) 2017 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 real_xxx (void); + +static void * +xxx_resolver (void) +{ + return &real_xxx; +} + +extern void xxx (void) __attribute__ ((ifunc ("xxx_resolver"))); diff --git a/elf/tst-ifuncmod1d.c b/elf/tst-ifuncmod1d.c new file mode 100644 index 0000000..5715ded --- /dev/null +++ b/elf/tst-ifuncmod1d.c @@ -0,0 +1,24 @@ +/* Test case for unsafe IFUNC resolver. + Copyright (C) 2017 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 +xxx (void) +{ +} + +__typeof (xxx) real_xxx __attribute__ ((alias("xxx"))); diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index f26a8b2..0c53c70 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -1085,6 +1085,32 @@ extern void _dl_non_dynamic_init (void) internal_function; extern void _dl_aux_init (ElfW(auxv_t) *av) internal_function; +# ifdef STDERR_FILENO +/* Define here after RTLD_PROGNAME is defined and if STDERR_FILENO is + defined. Since it is unsafe for IFUNC resolver to reference external + symbols, issue a fatal error when it happens. */ +static __always_inline void +dl_check_ifunc_resolver (struct link_map *sym_map, struct link_map *map, + const ElfW(Sym) *const refsym) +{ + if (sym_map != map + && sym_map->l_type != lt_executable + && !sym_map->l_relocated) + { + const char *strtab + = (const char *) D_PTR (map, l_info[DT_STRTAB]); + _dl_fatal_printf ("\ +%s: Relink `%s' with `%s' or place `%s' before `%s' for IFUNC symbol `%s'\n", + RTLD_PROGNAME, + map->l_libname->name, + sym_map->l_libname->name, + sym_map->l_libname->name, + map->l_libname->name, + strtab + refsym->st_name); + } +} +# endif + __END_DECLS #endif /* ldsodefs.h */ diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h index 6eca69d..3e6b995 100644 --- a/sysdeps/i386/dl-machine.h +++ b/sysdeps/i386/dl-machine.h @@ -323,18 +323,7 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc, && __builtin_expect (!skip_ifunc, 1)) { # ifndef RTLD_BOOTSTRAP - if (sym_map != map - && sym_map->l_type != lt_executable - && !sym_map->l_relocated) - { - const char *strtab - = (const char *) D_PTR (map, l_info[DT_STRTAB]); - _dl_fatal_printf ("\ -%s: Relink `%s' with `%s' for IFUNC symbol `%s'\n", - RTLD_PROGNAME, map->l_name, - sym_map->l_name, - strtab + refsym->st_name); - } + dl_check_ifunc_resolver (sym_map, map, refsym); # endif value = ((Elf32_Addr (*) (void)) value) (); } @@ -501,7 +490,12 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, && __builtin_expect (sym->st_shndx != SHN_UNDEF, 1) && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0) && __builtin_expect (!skip_ifunc, 1)) - value = ((Elf32_Addr (*) (void)) value) (); + { +# ifndef RESOLVE_CONFLICT_FIND_MAP + dl_check_ifunc_resolver (sym_map, map, refsym); +# endif + value = ((Elf32_Addr (*) (void)) value) (); + } switch (ELF32_R_TYPE (reloc->r_info)) { diff --git a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h index 3e7ae22..5737955 100644 --- a/sysdeps/x86_64/dl-machine.h +++ b/sysdeps/x86_64/dl-machine.h @@ -333,18 +333,7 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc, && __builtin_expect (!skip_ifunc, 1)) { # ifndef RTLD_BOOTSTRAP - if (sym_map != map - && sym_map->l_type != lt_executable - && !sym_map->l_relocated) - { - const char *strtab - = (const char *) D_PTR (map, l_info[DT_STRTAB]); - _dl_fatal_printf ("\ -%s: Relink `%s' with `%s' for IFUNC symbol `%s'\n", - RTLD_PROGNAME, map->l_name, - sym_map->l_name, - strtab + refsym->st_name); - } + dl_check_ifunc_resolver (sym_map, map, refsym); # endif value = ((ElfW(Addr) (*) (void)) value) (); } -- 2.9.3