Blob Blame History Raw
Short description: Work ld.so --verify crash on debuginfo files.
Author(s): Fedora glibc team <glibc@lists.fedoraproject.org>
Origin: PATCH
Bug-RHEL: #741105, #767146
Upstream status: not-needed

This change is designed to work around running ld.so on a debuginfo
file. This is the wrong fix for this problem and should be dropped.
The correct solution is to mark debuginfo files as new types of
ELF files.

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 24e2819345995bd9..007121144e71d9cf 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -880,6 +880,18 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
      in this information for the executable in case of an explicit
      loader invocation.  */
   struct r_file_id id;
+  struct stat64 st;
+
+  if (__glibc_unlikely (!_dl_get_file_id (fd, &id, &st)))
+    {
+      errstring = N_("cannot stat shared object");
+      call_lose_errno:
+        errval = errno;
+      call_lose:
+        lose (errval, fd, name, realname, l, errstring,
+              make_consistent ? r : NULL, nsid);
+    }
+
   if (mode & __RTLD_OPENEXEC)
     {
       assert (nsid == LM_ID_BASE);
@@ -887,16 +899,6 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
     }
   else
     {
-      if (__glibc_unlikely (!_dl_get_file_id (fd, &id)))
-	{
-	  errstring = N_("cannot stat shared object");
-	call_lose_errno:
-	  errval = errno;
-	call_lose:
-	  lose (errval, fd, name, realname, l, errstring,
-		make_consistent ? r : NULL, nsid);
-	}
-
       /* Look again to see if the real name matched another already loaded.  */
       for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
 	if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id))
@@ -1074,6 +1076,16 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
 		= N_("ELF load command address/offset not properly aligned");
 	      goto call_lose;
 	    }
+	  if (__glibc_unlikely (ph->p_offset + ph->p_filesz > st.st_size))
+	    {
+	      /* If the segment requires zeroing of part of its last
+		 page, we'll crash when accessing the unmapped page.
+		 There's still a possibility of a race, if the shared
+		 object is truncated between the fxstat above and the
+		 memset below.  */
+	      errstring = N_("ELF load command past end of file");
+	      goto call_lose;
+	    }
 
 	  struct loadcmd *c = &loadcmds[nloadcmds++];
 	  c->mapstart = ALIGN_DOWN (ph->p_vaddr, GLRO(dl_pagesize));
diff --git a/sysdeps/generic/dl-fileid.h b/sysdeps/generic/dl-fileid.h
index 6310d46c2a6efc60..209580b6c0b8524b 100644
--- a/sysdeps/generic/dl-fileid.h
+++ b/sysdeps/generic/dl-fileid.h
@@ -29,7 +29,8 @@ struct r_file_id
    On error, returns false, with errno set.  */
 static inline bool
 _dl_get_file_id (int fd __attribute__ ((unused)),
-		 struct r_file_id *id __attribute__ ((unused)))
+		 struct r_file_id *id __attribute__ ((unused)),
+		 struct stat64_t *st __attribute__((unused)))
 {
   return true;
 }
diff --git a/sysdeps/posix/dl-fileid.h b/sysdeps/posix/dl-fileid.h
index ae88a350788a5999..baa17b90a292a4d9 100644
--- a/sysdeps/posix/dl-fileid.h
+++ b/sysdeps/posix/dl-fileid.h
@@ -27,18 +27,16 @@ struct r_file_id
     ino64_t ino;
   };
 
-/* Sample FD to fill in *ID.  Returns true on success.
+/* Sample FD to fill in *ID and *ST.  Returns true on success.
    On error, returns false, with errno set.  */
 static inline bool
-_dl_get_file_id (int fd, struct r_file_id *id)
+_dl_get_file_id (int fd, struct r_file_id *id, struct stat64 *st)
 {
-  struct stat64 st;
-
-  if (__glibc_unlikely (__fxstat64 (_STAT_VER, fd, &st) < 0))
+  if (__glibc_unlikely (__fxstat64 (_STAT_VER, fd, st) < 0))
     return false;
 
-  id->dev = st.st_dev;
-  id->ino = st.st_ino;
+  id->dev = st->st_dev;
+  id->ino = st->st_ino;
   return true;
 }