520cc8
commit 198abcbb94618730dae1b3f4393efaa49e0ec8c7
520cc8
Author: Florian Weimer <fweimer@redhat.com>
520cc8
Date:   Mon Apr 11 11:30:31 2022 +0200
520cc8
520cc8
    Default to --with-default-link=no (bug 25812)
520cc8
520cc8
    This is necessary to place the libio vtables into the RELRO segment.
520cc8
    New tests elf/tst-relro-ldso and elf/tst-relro-libc are added to
520cc8
    verify that this is what actually happens.
520cc8
520cc8
    The new tests fail on ia64 due to lack of (default) RELRO support
520cc8
    inbutils, so they are XFAILed there.
520cc8
520cc8
Conflicts:
520cc8
	elf/Makefile
520cc8
	  (missing valgrind smoke test)
520cc8
520cc8
diff --git a/INSTALL b/INSTALL
520cc8
index b3a4370f592c5047..b69672b283c0b774 100644
520cc8
--- a/INSTALL
520cc8
+++ b/INSTALL
520cc8
@@ -90,6 +90,12 @@ if 'CFLAGS' is specified it must enable optimization.  For example:
520cc8
      library will still be usable, but functionality may be lost--for
520cc8
      example, you can't build a shared libc with old binutils.
520cc8
 
520cc8
+'--with-default-link=FLAG'
520cc8
+     With '--with-default-link=yes', the build system does not use a
520cc8
+     custom linker script for linking shared objects.  The default for
520cc8
+     FLAG is the opposite, 'no', because the custom linker script is
520cc8
+     needed for full RELRO protection.
520cc8
+
520cc8
 '--with-nonshared-cflags=CFLAGS'
520cc8
      Use additional compiler flags CFLAGS to build the parts of the
520cc8
      library which are always statically linked into applications and
520cc8
diff --git a/configure b/configure
520cc8
index 8b3681d2e28310c8..c794cea4359b3da3 100755
520cc8
--- a/configure
520cc8
+++ b/configure
520cc8
@@ -3339,7 +3339,7 @@ fi
520cc8
 if test "${with_default_link+set}" = set; then :
520cc8
   withval=$with_default_link; use_default_link=$withval
520cc8
 else
520cc8
-  use_default_link=default
520cc8
+  use_default_link=no
520cc8
 fi
520cc8
 
520cc8
 
520cc8
@@ -5965,69 +5965,6 @@ fi
520cc8
 $as_echo "$libc_cv_hashstyle" >&6; }
520cc8
 
520cc8
 
520cc8
-# The linker's default -shared behavior is good enough if it
520cc8
-# does these things that our custom linker scripts ensure that
520cc8
-# all allocated NOTE sections come first.
520cc8
-if test "$use_default_link" = default; then
520cc8
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sufficient default -shared layout" >&5
520cc8
-$as_echo_n "checking for sufficient default -shared layout... " >&6; }
520cc8
-if ${libc_cv_use_default_link+:} false; then :
520cc8
-  $as_echo_n "(cached) " >&6
520cc8
-else
520cc8
-    libc_cv_use_default_link=no
520cc8
-  cat > conftest.s <<\EOF
520cc8
-	  .section .note.a,"a",%note
520cc8
-	  .balign 4
520cc8
-	  .long 4,4,9
520cc8
-	  .string "GNU"
520cc8
-	  .string "foo"
520cc8
-	  .section .note.b,"a",%note
520cc8
-	  .balign 4
520cc8
-	  .long 4,4,9
520cc8
-	  .string "GNU"
520cc8
-	  .string "bar"
520cc8
-EOF
520cc8
-  if { ac_try='  ${CC-cc} $ASFLAGS -shared -o conftest.so conftest.s 1>&5'
520cc8
-  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
520cc8
-  (eval $ac_try) 2>&5
520cc8
-  ac_status=$?
520cc8
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
520cc8
-  test $ac_status = 0; }; } &&
520cc8
-       ac_try=`$READELF -S conftest.so | sed -n \
520cc8
-	 '${x;p;}
520cc8
-	  s/^ *\[ *[1-9][0-9]*\]  *\([^ ][^ ]*\)  *\([^ ][^ ]*\) .*$/\2 \1/
520cc8
-	  t a
520cc8
-	  b
520cc8
-	  : a
520cc8
-	  H'`
520cc8
-  then
520cc8
-    libc_seen_a=no libc_seen_b=no
520cc8
-    set -- $ac_try
520cc8
-    while test $# -ge 2 -a "$1" = NOTE; do
520cc8
-      case "$2" in
520cc8
-      .note.a) libc_seen_a=yes ;;
520cc8
-      .note.b) libc_seen_b=yes ;;
520cc8
-      esac
520cc8
-      shift 2
520cc8
-    done
520cc8
-    case "$libc_seen_a$libc_seen_b" in
520cc8
-    yesyes)
520cc8
-      libc_cv_use_default_link=yes
520cc8
-      ;;
520cc8
-    *)
520cc8
-      echo >&5 "\
520cc8
-$libc_seen_a$libc_seen_b from:
520cc8
-$ac_try"
520cc8
-      ;;
520cc8
-    esac
520cc8
-  fi
520cc8
-  rm -f conftest*
520cc8
-fi
520cc8
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_use_default_link" >&5
520cc8
-$as_echo "$libc_cv_use_default_link" >&6; }
520cc8
-  use_default_link=$libc_cv_use_default_link
520cc8
-fi
520cc8
-
520cc8
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GLOB_DAT reloc" >&5
520cc8
 $as_echo_n "checking for GLOB_DAT reloc... " >&6; }
520cc8
 if ${libc_cv_has_glob_dat+:} false; then :
520cc8
diff --git a/configure.ac b/configure.ac
520cc8
index 82d9ab2fb67145bb..52429d82344954b3 100644
520cc8
--- a/configure.ac
520cc8
+++ b/configure.ac
520cc8
@@ -152,7 +152,7 @@ AC_ARG_WITH([default-link],
520cc8
 	    AC_HELP_STRING([--with-default-link],
520cc8
 			   [do not use explicit linker scripts]),
520cc8
 	    [use_default_link=$withval],
520cc8
-	    [use_default_link=default])
520cc8
+	    [use_default_link=no])
520cc8
 
520cc8
 dnl Additional build flags injection.
520cc8
 AC_ARG_WITH([nonshared-cflags],
520cc8
@@ -1352,59 +1352,6 @@ fi
520cc8
 rm -f conftest*])
520cc8
 AC_SUBST(libc_cv_hashstyle)
520cc8
 
520cc8
-# The linker's default -shared behavior is good enough if it
520cc8
-# does these things that our custom linker scripts ensure that
520cc8
-# all allocated NOTE sections come first.
520cc8
-if test "$use_default_link" = default; then
520cc8
-  AC_CACHE_CHECK([for sufficient default -shared layout],
520cc8
-		  libc_cv_use_default_link, [dnl
520cc8
-  libc_cv_use_default_link=no
520cc8
-  cat > conftest.s <<\EOF
520cc8
-	  .section .note.a,"a",%note
520cc8
-	  .balign 4
520cc8
-	  .long 4,4,9
520cc8
-	  .string "GNU"
520cc8
-	  .string "foo"
520cc8
-	  .section .note.b,"a",%note
520cc8
-	  .balign 4
520cc8
-	  .long 4,4,9
520cc8
-	  .string "GNU"
520cc8
-	  .string "bar"
520cc8
-EOF
520cc8
-  if AC_TRY_COMMAND([dnl
520cc8
-  ${CC-cc} $ASFLAGS -shared -o conftest.so conftest.s 1>&AS_MESSAGE_LOG_FD]) &&
520cc8
-       ac_try=`$READELF -S conftest.so | sed -n \
520cc8
-	 ['${x;p;}
520cc8
-	  s/^ *\[ *[1-9][0-9]*\]  *\([^ ][^ ]*\)  *\([^ ][^ ]*\) .*$/\2 \1/
520cc8
-	  t a
520cc8
-	  b
520cc8
-	  : a
520cc8
-	  H']`
520cc8
-  then
520cc8
-    libc_seen_a=no libc_seen_b=no
520cc8
-    set -- $ac_try
520cc8
-    while test $# -ge 2 -a "$1" = NOTE; do
520cc8
-      case "$2" in
520cc8
-      .note.a) libc_seen_a=yes ;;
520cc8
-      .note.b) libc_seen_b=yes ;;
520cc8
-      esac
520cc8
-      shift 2
520cc8
-    done
520cc8
-    case "$libc_seen_a$libc_seen_b" in
520cc8
-    yesyes)
520cc8
-      libc_cv_use_default_link=yes
520cc8
-      ;;
520cc8
-    *)
520cc8
-      echo >&AS_MESSAGE_LOG_FD "\
520cc8
-$libc_seen_a$libc_seen_b from:
520cc8
-$ac_try"
520cc8
-      ;;
520cc8
-    esac
520cc8
-  fi
520cc8
-  rm -f conftest*])
520cc8
-  use_default_link=$libc_cv_use_default_link
520cc8
-fi
520cc8
-
520cc8
 AC_CACHE_CHECK(for GLOB_DAT reloc,
520cc8
 	       libc_cv_has_glob_dat, [dnl
520cc8
 cat > conftest.c <
520cc8
diff --git a/elf/Makefile b/elf/Makefile
520cc8
index 89ce4f5196e5eb39..1fdf40cbd49e233e 100644
520cc8
--- a/elf/Makefile
520cc8
+++ b/elf/Makefile
520cc8
@@ -477,6 +477,40 @@ tests-execstack-yes = \
520cc8
   # tests-execstack-yes
520cc8
 endif
520cc8
 endif
520cc8
+
520cc8
+tests-special += $(objpfx)tst-relro-ldso.out $(objpfx)tst-relro-libc.out
520cc8
+$(objpfx)tst-relro-ldso.out: tst-relro-symbols.py $(..)/scripts/glibcelf.py \
520cc8
+  $(objpfx)ld.so
520cc8
+	$(PYTHON) tst-relro-symbols.py $(objpfx)ld.so \
520cc8
+	  --required=_rtld_global_ro \
520cc8
+	  > $@ 2>&1; $(evaluate-test)
520cc8
+# The optional symbols are present in libc only if the architecture has
520cc8
+# the GLIBC_2.0 symbol set in libc.
520cc8
+$(objpfx)tst-relro-libc.out: tst-relro-symbols.py $(..)/scripts/glibcelf.py \
520cc8
+  $(common-objpfx)libc.so
520cc8
+	$(PYTHON) tst-relro-symbols.py $(common-objpfx)libc.so \
520cc8
+	    --required=_IO_cookie_jumps \
520cc8
+	    --required=_IO_file_jumps \
520cc8
+	    --required=_IO_file_jumps_maybe_mmap \
520cc8
+	    --required=_IO_file_jumps_mmap \
520cc8
+	    --required=_IO_helper_jumps \
520cc8
+	    --required=_IO_mem_jumps \
520cc8
+	    --required=_IO_obstack_jumps \
520cc8
+	    --required=_IO_proc_jumps \
520cc8
+	    --required=_IO_str_chk_jumps \
520cc8
+	    --required=_IO_str_jumps \
520cc8
+	    --required=_IO_strn_jumps \
520cc8
+	    --required=_IO_wfile_jumps \
520cc8
+	    --required=_IO_wfile_jumps_maybe_mmap \
520cc8
+	    --required=_IO_wfile_jumps_mmap \
520cc8
+	    --required=_IO_wmem_jumps \
520cc8
+	    --required=_IO_wstr_jumps \
520cc8
+	    --required=_IO_wstrn_jumps \
520cc8
+	    --optional=_IO_old_cookie_jumps \
520cc8
+	    --optional=_IO_old_file_jumps \
520cc8
+	    --optional=_IO_old_proc_jumps \
520cc8
+	  > $@ 2>&1; $(evaluate-test)
520cc8
+
520cc8
 tests += $(tests-execstack-$(have-z-execstack))
520cc8
 ifeq ($(run-built-tests),yes)
520cc8
 tests-special += \
520cc8
diff --git a/elf/tst-relro-symbols.py b/elf/tst-relro-symbols.py
520cc8
new file mode 100644
520cc8
index 0000000000000000..368ea3349f86bd81
520cc8
--- /dev/null
520cc8
+++ b/elf/tst-relro-symbols.py
520cc8
@@ -0,0 +1,137 @@
520cc8
+#!/usr/bin/python3
520cc8
+# Verify that certain symbols are covered by RELRO.
520cc8
+# Copyright (C) 2022 Free Software Foundation, Inc.
520cc8
+# This file is part of the GNU C Library.
520cc8
+#
520cc8
+# The GNU C Library is free software; you can redistribute it and/or
520cc8
+# modify it under the terms of the GNU Lesser General Public
520cc8
+# License as published by the Free Software Foundation; either
520cc8
+# version 2.1 of the License, or (at your option) any later version.
520cc8
+#
520cc8
+# The GNU C Library is distributed in the hope that it will be useful,
520cc8
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
520cc8
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
520cc8
+# Lesser General Public License for more details.
520cc8
+#
520cc8
+# You should have received a copy of the GNU Lesser General Public
520cc8
+# License along with the GNU C Library; if not, see
520cc8
+# <https://www.gnu.org/licenses/>.
520cc8
+
520cc8
+"""Analyze a (shared) object to verify that certain symbols are
520cc8
+present and covered by the PT_GNU_RELRO segment.
520cc8
+
520cc8
+"""
520cc8
+
520cc8
+import argparse
520cc8
+import os.path
520cc8
+import sys
520cc8
+
520cc8
+# Make available glibc Python modules.
520cc8
+sys.path.append(os.path.join(
520cc8
+    os.path.dirname(os.path.realpath(__file__)), os.path.pardir, 'scripts'))
520cc8
+
520cc8
+import glibcelf
520cc8
+
520cc8
+def find_relro(path: str, img: glibcelf.Image) -> (int, int):
520cc8
+    """Discover the address range of the PT_GNU_RELRO segment."""
520cc8
+    for phdr in img.phdrs():
520cc8
+        if phdr.p_type == glibcelf.Pt.PT_GNU_RELRO:
520cc8
+            # The computation is not entirely accurate because
520cc8
+            # _dl_protect_relro in elf/dl-reloc.c rounds both the
520cc8
+            # start end and downwards using the run-time page size.
520cc8
+            return phdr.p_vaddr, phdr.p_vaddr + phdr.p_memsz
520cc8
+    sys.stdout.write('{}: error: no PT_GNU_RELRO segment\n'.format(path))
520cc8
+    sys.exit(1)
520cc8
+
520cc8
+def check_in_relro(kind, relro_begin, relro_end, name, start, size, error):
520cc8
+    """Check if a section or symbol falls within in the RELRO segment."""
520cc8
+    end = start + size - 1
520cc8
+    if not (relro_begin <= start < end < relro_end):
520cc8
+        error(
520cc8
+            '{} {!r} of size {} at 0x{:x} is not in RELRO range [0x{:x}, 0x{:x})'.format(
520cc8
+                kind, name.decode('UTF-8'), start, size,
520cc8
+                relro_begin, relro_end))
520cc8
+
520cc8
+def get_parser():
520cc8
+    """Return an argument parser for this script."""
520cc8
+    parser = argparse.ArgumentParser(description=__doc__)
520cc8
+    parser.add_argument('object', help='path to object file to check')
520cc8
+    parser.add_argument('--required', metavar='NAME', default=(),
520cc8
+                        help='required symbol names', nargs='*')
520cc8
+    parser.add_argument('--optional', metavar='NAME', default=(),
520cc8
+                        help='required symbol names', nargs='*')
520cc8
+    return parser
520cc8
+
520cc8
+def main(argv):
520cc8
+    """The main entry point."""
520cc8
+    parser = get_parser()
520cc8
+    opts = parser.parse_args(argv)
520cc8
+    img = glibcelf.Image.readfile(opts.object)
520cc8
+
520cc8
+    required_symbols = frozenset([sym.encode('UTF-8')
520cc8
+                                  for sym in opts.required])
520cc8
+    optional_symbols = frozenset([sym.encode('UTF-8')
520cc8
+                                  for sym in opts.optional])
520cc8
+    check_symbols = required_symbols | optional_symbols
520cc8
+
520cc8
+    # Tracks the symbols in check_symbols that have been found.
520cc8
+    symbols_found = set()
520cc8
+
520cc8
+    # Discover the extent of the RELRO segment.
520cc8
+    relro_begin, relro_end = find_relro(opts.object, img)
520cc8
+    symbol_table_found = False
520cc8
+
520cc8
+    errors = False
520cc8
+    def error(msg: str) -> None:
520cc8
+        """Record an error condition and write a message to standard output."""
520cc8
+        nonlocal errors
520cc8
+        errors = True
520cc8
+        sys.stdout.write('{}: error: {}\n'.format(opts.object, msg))
520cc8
+
520cc8
+    # Iterate over section headers to find the symbol table.
520cc8
+    for shdr in img.shdrs():
520cc8
+        if shdr.sh_type == glibcelf.Sht.SHT_SYMTAB:
520cc8
+            symbol_table_found = True
520cc8
+            for sym in img.syms(shdr):
520cc8
+                if sym.st_name in check_symbols:
520cc8
+                    symbols_found.add(sym.st_name)
520cc8
+
520cc8
+                    # Validate symbol type, section, and size.
520cc8
+                    if sym.st_info.type != glibcelf.Stt.STT_OBJECT:
520cc8
+                        error('symbol {!r} has wrong type {}'.format(
520cc8
+                            sym.st_name.decode('UTF-8'), sym.st_info.type))
520cc8
+                    if sym.st_shndx in glibcelf.Shn:
520cc8
+                        error('symbol {!r} has reserved section {}'.format(
520cc8
+                            sym.st_name.decode('UTF-8'), sym.st_shndx))
520cc8
+                        continue
520cc8
+                    if sym.st_size == 0:
520cc8
+                        error('symbol {!r} has size zero'.format(
520cc8
+                            sym.st_name.decode('UTF-8')))
520cc8
+                        continue
520cc8
+
520cc8
+                    check_in_relro('symbol', relro_begin, relro_end,
520cc8
+                                   sym.st_name, sym.st_value, sym.st_size,
520cc8
+                                   error)
520cc8
+            continue # SHT_SYMTAB
520cc8
+        if shdr.sh_name == b'.data.rel.ro' \
520cc8
+           or shdr.sh_name.startswith(b'.data.rel.ro.'):
520cc8
+            check_in_relro('section', relro_begin, relro_end,
520cc8
+                           shdr.sh_name, shdr.sh_addr, shdr.sh_size,
520cc8
+                           error)
520cc8
+            continue
520cc8
+
520cc8
+    if required_symbols - symbols_found:
520cc8
+        for sym in sorted(required_symbols - symbols_found):
520cc8
+            error('symbol {!r} not found'.format(sym.decode('UTF-8')))
520cc8
+
520cc8
+    if errors:
520cc8
+        sys.exit(1)
520cc8
+
520cc8
+    if not symbol_table_found:
520cc8
+        sys.stdout.write(
520cc8
+            '{}: warning: no symbol table found (stripped object)\n'.format(
520cc8
+                opts.object))
520cc8
+        sys.exit(77)
520cc8
+
520cc8
+if __name__ == '__main__':
520cc8
+    main(sys.argv[1:])
520cc8
diff --git a/manual/install.texi b/manual/install.texi
520cc8
index c262fd56d0cef67b..a2c43bd692de7825 100644
520cc8
--- a/manual/install.texi
520cc8
+++ b/manual/install.texi
520cc8
@@ -117,6 +117,12 @@ problem and suppress these constructs, so that the library will still be
520cc8
 usable, but functionality may be lost---for example, you can't build a
520cc8
 shared libc with old binutils.
520cc8
 
520cc8
+@item --with-default-link=@var{FLAG}
520cc8
+With @code{--with-default-link=yes}, the build system does not use a
520cc8
+custom linker script for linking shared objects.  The default for
520cc8
+@var{FLAG} is the opposite, @samp{no}, because the custom linker script
520cc8
+is needed for full RELRO protection.
520cc8
+
520cc8
 @item --with-nonshared-cflags=@var{cflags}
520cc8
 Use additional compiler flags @var{cflags} to build the parts of the
520cc8
 library which are always statically linked into applications and
520cc8
diff --git a/sysdeps/unix/sysv/linux/ia64/Makefile b/sysdeps/unix/sysv/linux/ia64/Makefile
520cc8
index 97fc7df0b122d6a0..b1ad1ab7b1efa34c 100644
520cc8
--- a/sysdeps/unix/sysv/linux/ia64/Makefile
520cc8
+++ b/sysdeps/unix/sysv/linux/ia64/Makefile
520cc8
@@ -1,3 +1,9 @@
520cc8
+ifeq ($(subdir),elf)
520cc8
+# ia64 does not support PT_GNU_RELRO.
520cc8
+test-xfail-tst-relro-ldso = yes
520cc8
+test-xfail-tst-relro-libc = yes
520cc8
+endif
520cc8
+
520cc8
 ifeq ($(subdir),misc)
520cc8
 sysdep_headers += sys/rse.h
520cc8
 endif