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