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