public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] ld.so: Handle read-only dynamic section gracefully [BZ #28340]
@ 2021-09-14 19:09 Siddhesh Poyarekar
  2021-09-14 19:15 ` Florian Weimer
  2021-09-15 14:35 ` H.J. Lu
  0 siblings, 2 replies; 41+ messages in thread
From: Siddhesh Poyarekar @ 2021-09-14 19:09 UTC (permalink / raw)
  To: libc-alpha; +Cc: fweimer, carlos

ld.so on x86_64 (and possibly for all other architectures that don't
have a read-only dynamic section by default but I haven't checked on
all of them), when invoked with a DSO that has a read-only .dynamic
section, crashes when trying to update gotplt, symtab, etc. entries
with the load address.  This crash happens even during verification,
i.e. when it is invoked with --verify or with LD_TRACE_LOADED_OBJECTS.

This is seen with vdso64.so from the Linux kernel on x86_64 for
example.

Handle this case more gracefully by bailing out early when a read-only
PT_DYNAMIC segment is encountered and if the dynamic section has
entries that would need to be updated on relocation.  A test case has
also been added to verify that BZ #28340 has been fixed.
---
 elf/Makefile             | 11 +++++++++--
 elf/dl-load.c            | 14 ++++++++++++++
 elf/get-dynamic-info.h   | 41 ++++++++++++++++++++++++++++++++++++++++
 elf/rtld.c               | 12 ++++++++++++
 elf/tst-ro-dynamic-mod.c |  1 +
 elf/tst-ro-dynamic.map   | 13 +++++++++++++
 elf/tst-ro-dynamic.sh    | 36 +++++++++++++++++++++++++++++++++++
 7 files changed, 126 insertions(+), 2 deletions(-)
 create mode 100644 elf/tst-ro-dynamic-mod.c
 create mode 100644 elf/tst-ro-dynamic.map
 create mode 100644 elf/tst-ro-dynamic.sh

diff --git a/elf/Makefile b/elf/Makefile
index 9f3fadc37e..0bed622040 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -223,7 +223,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
 	 tst-tls-ie tst-tls-ie-dlmopen argv0test \
 	 tst-glibc-hwcaps tst-glibc-hwcaps-prepend tst-glibc-hwcaps-mask \
 	 tst-tls20 tst-tls21 tst-dlmopen-dlerror tst-dlmopen-gethostbyname \
-	 tst-dl-is_dso
+	 tst-dl-is_dso tst-ro-dynamic
 #	 reldep9
 tests-internal += loadtest unload unload2 circleload1 \
 	 neededtest neededtest2 neededtest3 neededtest4 \
@@ -359,7 +359,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		libmarkermod4-1 libmarkermod4-2 libmarkermod4-3 libmarkermod4-4 \
 		tst-tls20mod-bad tst-tls21mod tst-dlmopen-dlerror-mod \
 		tst-auxvalmod \
-		tst-dlmopen-gethostbyname-mod \
+		tst-dlmopen-gethostbyname-mod tst-ro-dynamic-mod \
 
 # Most modules build with _ISOMAC defined, but those filtered out
 # depend on internal headers.
@@ -1908,3 +1908,10 @@ $(objpfx)tst-getauxval-static.out: $(objpfx)tst-auxvalmod.so
 tst-getauxval-static-ENV = LD_LIBRARY_PATH=$(objpfx):$(common-objpfx)
 
 $(objpfx)tst-dlmopen-gethostbyname.out: $(objpfx)tst-dlmopen-gethostbyname-mod.so
+
+LDFLAGS-tst-ro-dynamic-mod = -Wl,--version-script=tst-ro-dynamic.map
+$(objpfx)tst-ro-dynamic-mod.so: tst-ro-dynamic.map
+$(objpfx)tst-ro-dynamic.out: tst-ro-dynamic.sh $(objpfx)tst-ro-dynamic-mod.so \
+			     $(objpfx)ld.so
+	$(SHELL) $^ '$(test-wrapper-env)' '$(run_program_env)' > $@; \
+	$(evaluate-test)
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 650e4edc35..5bac007a01 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1124,6 +1124,9 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
     * executable.  Other platforms default to a nonexecutable stack and don't
     * need PT_GNU_STACK to do so.  */
    uint_fast16_t stack_flags = DEFAULT_STACK_PERMS;
+#ifndef DL_RO_DYN_SECTION
+  bool readonly_dynamic = false;
+#endif
 
   {
     /* Scan the program header table, collecting its load commands.  */
@@ -1149,6 +1152,9 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
 		 such a segment to avoid a crash later.  */
 	      l->l_ld = (void *) ph->p_vaddr;
 	      l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn));
+#ifndef DL_RO_DYN_SECTION
+	      readonly_dynamic = (ph->p_flags & PF_W) == 0;
+#endif
 	    }
 	  break;
 
@@ -1292,6 +1298,14 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
   else
     l->l_ld = (ElfW(Dyn) *) ((ElfW(Addr)) l->l_ld + l->l_addr);
 
+#ifndef DL_RO_DYN_SECTION
+  if (readonly_dynamic && elf_dynamic_info_needs_fixup (l))
+    {
+      errstring = N_("dynamic section of object file not writable");
+      goto lose;
+    }
+#endif
+
   elf_get_dynamic_info (l, NULL);
 
   /* Make sure we are not dlopen'ing an object that has the
diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h
index d8ec32377d..6072696afb 100644
--- a/elf/get-dynamic-info.h
+++ b/elf/get-dynamic-info.h
@@ -22,6 +22,47 @@
 #include <assert.h>
 #include <libc-diag.h>
 
+#ifndef RESOLVE_MAP
+static
+#else
+auto
+#endif
+#ifndef DL_RO_DYN_SECTION
+inline bool __attribute__ ((unused, always_inline))
+elf_dynamic_info_needs_fixup (struct link_map *l)
+{
+#if __ELF_NATIVE_CLASS == 32
+  typedef Elf32_Word d_tag_utype;
+#elif __ELF_NATIVE_CLASS == 64
+  typedef Elf64_Xword d_tag_utype;
+#endif
+
+  if (l->l_addr == 0)
+    return false;
+
+  for (ElfW(Dyn) *dyn = l->l_ld; dyn->d_tag != DT_NULL; dyn++)
+    switch ((d_tag_utype) dyn->d_tag)
+      {
+      case DT_HASH:
+      case DT_PLTGOT:
+      case DT_STRTAB:
+      case DT_SYMTAB:
+# if !ELF_MACHINE_NO_RELA
+      case DT_RELA:
+# endif
+# if !ELF_MACHINE_NO_REL
+      case DT_REL:
+# endif
+      case DT_JMPREL:
+      case VERSYMIDX (DT_VERSYM):
+      case ADDRIDX (DT_GNU_HASH):
+	return true;
+      }
+
+  return false;
+}
+#endif
+
 #ifndef RESOLVE_MAP
 static
 #else
diff --git a/elf/rtld.c b/elf/rtld.c
index 878e6480f4..8067a01088 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1456,6 +1456,10 @@ dl_main (const ElfW(Phdr) *phdr,
   /* And it was opened directly.  */
   ++main_map->l_direct_opencount;
 
+#ifndef DL_RO_DYN_SECTION
+  bool readonly_dynamic = false;
+#endif
+
   /* Scan the program header table for the dynamic section.  */
   for (ph = phdr; ph < &phdr[phnum]; ++ph)
     switch (ph->p_type)
@@ -1468,6 +1472,9 @@ dl_main (const ElfW(Phdr) *phdr,
 	/* This tells us where to find the dynamic section,
 	   which tells us everything we need to do.  */
 	main_map->l_ld = (void *) main_map->l_addr + ph->p_vaddr;
+#ifndef DL_RO_DYN_SECTION
+	readonly_dynamic = (ph->p_flags & PF_W) == 0;
+#endif
 	break;
       case PT_INTERP:
 	/* This "interpreter segment" was used by the program loader to
@@ -1612,6 +1619,11 @@ dl_main (const ElfW(Phdr) *phdr,
 
   if (! rtld_is_main)
     {
+#ifndef DL_RO_DYN_SECTION
+      if (readonly_dynamic && elf_dynamic_info_needs_fixup (main_map))
+	_dl_fatal_printf ("dynamic section of executable is not writable\n");
+#endif
+
       /* Extract the contents of the dynamic section for easy access.  */
       elf_get_dynamic_info (main_map, NULL);
 
diff --git a/elf/tst-ro-dynamic-mod.c b/elf/tst-ro-dynamic-mod.c
new file mode 100644
index 0000000000..2bc87d0c3c
--- /dev/null
+++ b/elf/tst-ro-dynamic-mod.c
@@ -0,0 +1 @@
+int foo = 0;
diff --git a/elf/tst-ro-dynamic.map b/elf/tst-ro-dynamic.map
new file mode 100644
index 0000000000..dac92e0b94
--- /dev/null
+++ b/elf/tst-ro-dynamic.map
@@ -0,0 +1,13 @@
+SECTIONS
+{
+  .data : { *(.data) } :text
+  .bss : { *(.bss) } :text
+  .dynamic : { *(.dynamic) } :text :dynamic
+  .text : { *(.text) } :text
+}
+
+PHDRS
+{
+  text PT_LOAD FLAGS(5) FILEHDR PHDRS;
+  dynamic PT_DYNAMIC FLAGS(4);
+}
diff --git a/elf/tst-ro-dynamic.sh b/elf/tst-ro-dynamic.sh
new file mode 100644
index 0000000000..3d7144e0e2
--- /dev/null
+++ b/elf/tst-ro-dynamic.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+# Ensure ld.so does not crash when verifying DSO with read-only dynamic
+# section.
+# Copyright (C) 2021 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <https://www.gnu.org/licenses/>.
+
+dso=$1
+rtld=$2
+test_wrapper_env=$3
+run_program_env=$4
+
+${test_wrapper_env} \
+${run_program_env} \
+$rtld --verify ${dso}
+
+ret=$?
+
+# ld.so should fail gracefully by returning 1.
+if [ $ret -ne 1 ]; then
+  echo "ld.so returned $ret"
+  exit 1
+fi
-- 
2.31.1


^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2021-10-14 12:36 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-14 19:09 [PATCH] ld.so: Handle read-only dynamic section gracefully [BZ #28340] Siddhesh Poyarekar
2021-09-14 19:15 ` Florian Weimer
2021-09-15  1:14   ` Siddhesh Poyarekar
2021-09-15 14:35 ` H.J. Lu
2021-09-15 15:42   ` Siddhesh Poyarekar
2021-09-15 16:13     ` H.J. Lu
2021-09-15 16:24       ` Siddhesh Poyarekar
2021-09-15 16:34         ` H.J. Lu
2021-09-16  1:43           ` Siddhesh Poyarekar
2021-09-16  2:23             ` H.J. Lu
2021-09-16  3:46               ` Siddhesh Poyarekar
2021-09-16  4:26                 ` H.J. Lu
2021-09-16  4:28                   ` Siddhesh Poyarekar
2021-09-16  4:30                     ` H.J. Lu
2021-09-16  4:48               ` Florian Weimer
2021-09-16  5:36                 ` Siddhesh Poyarekar
2021-09-16  5:46                   ` Florian Weimer
2021-09-16  6:04                     ` Siddhesh Poyarekar
2021-09-16 14:11                 ` Carlos O'Donell
2021-09-16 15:18                   ` H.J. Lu
2021-09-16 16:45                     ` Siddhesh Poyarekar
2021-09-16 17:38                       ` H.J. Lu
2021-09-16 17:58                         ` Siddhesh Poyarekar
2021-09-16 22:11                           ` H.J. Lu
2021-09-17  2:47                             ` Siddhesh Poyarekar
2021-09-17  2:59                               ` H.J. Lu
2021-09-17  3:36                                 ` Siddhesh Poyarekar
2021-09-17  3:42                                   ` H.J. Lu
2021-09-17  3:44                                     ` Siddhesh Poyarekar
2021-09-17  3:44                                   ` Florian Weimer
2021-09-17  3:51                                     ` Siddhesh Poyarekar
2021-09-16 18:03                         ` Florian Weimer
2021-09-16 22:14                           ` H.J. Lu
2021-09-17  2:58                           ` Siddhesh Poyarekar
2021-09-17  3:46                             ` Florian Weimer
2021-09-17  4:00                               ` Florian Weimer
2021-09-17  4:12                                 ` [PATCH] ld.so: Remove DL_RO_DYN_SECTION H.J. Lu
2021-09-17  6:54                                   ` David Abdurachmanov
2021-09-17  9:01                                   ` Siddhesh Poyarekar
2021-09-17 15:40                                     ` H.J. Lu
2021-10-14 12:36                               ` [PATCH] ld.so: Handle read-only dynamic section gracefully [BZ #28340] Maciej W. Rozycki

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).