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