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