about summary refs log tree commit diff
path: root/elf/dl-load.c
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2000-10-21 17:08:18 +0000
committerUlrich Drepper <drepper@redhat.com>2000-10-21 17:08:18 +0000
commita35e137a99c4f06387c38f071d9b49a89cf884b5 (patch)
treefa92c2139527594c50d709f2eedc29d8191c72a0 /elf/dl-load.c
parentd1990c556227b4ab884ee562e91000e6c74c98d8 (diff)
downloadglibc-a35e137a99c4f06387c38f071d9b49a89cf884b5.tar.gz
glibc-a35e137a99c4f06387c38f071d9b49a89cf884b5.tar.xz
glibc-a35e137a99c4f06387c38f071d9b49a89cf884b5.zip
Update.
	* elf/dl-load.c (_dl_map_object_from_fd): Split out ELF file
	verification in open_verify.
	(open_verify): New function.  Called instead of open.  Ignores valid
	files for other architectures.
	(open_path): Call open_verify instead of open.
	(_dl_map_object): Likewise.
	Somewhat based on a patch by Don Dugger <n0ano@valinux.com>.

	* io/pwd.c (main): The output was missing a newline.
Diffstat (limited to 'elf/dl-load.c')
-rw-r--r--elf/dl-load.c252
1 files changed, 156 insertions, 96 deletions
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 978a81c30d..6f5f33657a 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -92,6 +92,24 @@ ELF_PREFERRED_ADDRESS_DATA;
 # define ELF_FIXED_ADDRESS(loader, mapstart) ((void) 0)
 #endif
 
+/* Type for the buffer we put the ELF header and hopefully the program
+   header.  This buffer does not really have to be too large.  In most
+   cases the program header follows the ELF header directly.  If this
+   is not the case all bets are off and we can make the header arbitrarily
+   large and still won't get it read.  This means the only question is
+   how large are the ELF and program header combined.  The ELF header
+   in 64-bit files is 56 bytes long.  Each program header entry is again
+   56 bytes long.  I.e., even with a file which has 17 program header
+   entries we only have to read 1kB.  And 17 program header entries is
+   plenty, normal files have < 10.  If this heuristic should really fail
+   for some file the code in `_dl_map_object_from_fd' knows how to
+   recover.  */
+struct filebuf
+{
+  ssize_t len;
+  char buf[1024];
+};
+
 size_t _dl_pagesize;
 
 extern const char *_dl_platform;
@@ -724,29 +742,10 @@ lose (int code, int fd, const char *name, char *realname, struct link_map *l,
 static
 #endif
 struct link_map *
-_dl_map_object_from_fd (const char *name, int fd, char *realname,
-			struct link_map *loader, int l_type, int mode)
+_dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp,
+			char *realname, struct link_map *loader, int l_type,
+			int mode)
 {
-  /* This is the expected ELF header.  */
-#define ELF32_CLASS ELFCLASS32
-#define ELF64_CLASS ELFCLASS64
-#ifndef VALID_ELF_HEADER
-# define VALID_ELF_HEADER(hdr,exp,size)	(memcmp (hdr, exp, size) == 0)
-# define VALID_ELF_OSABI(osabi)		(osabi == ELFOSABI_SYSV)
-# define VALID_ELF_ABIVERSION(ver)	(ver == 0)
-#endif
-  static const unsigned char expected[EI_PAD] =
-  {
-    [EI_MAG0] = ELFMAG0,
-    [EI_MAG1] = ELFMAG1,
-    [EI_MAG2] = ELFMAG2,
-    [EI_MAG3] = ELFMAG3,
-    [EI_CLASS] = ELFW(CLASS),
-    [EI_DATA] = byteorder,
-    [EI_VERSION] = EV_CURRENT,
-    [EI_OSABI] = ELFOSABI_SYSV,
-    [EI_ABIVERSION] = 0
-  };
   struct link_map *l = NULL;
 
   inline caddr_t map_segment (ElfW(Addr) mapstart, size_t len,
@@ -765,8 +764,6 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
   const ElfW(Phdr) *ph;
   size_t maplength;
   int type;
-  char *readbuf;
-  ssize_t readlength;
   struct stat64 st;
 
   /* Get file information.  */
@@ -804,64 +801,8 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
   if (__builtin_expect (_dl_debug_files, 0))
     _dl_debug_message (1, "file=", name, ";  generating link map\n", NULL);
 
-  /* Read the header directly.  */
-  readbuf = alloca (_dl_pagesize);
-  readlength = __libc_read (fd, readbuf, _dl_pagesize);
-  if (readlength < (ssize_t) sizeof (*header))
-    LOSE (errno, N_("cannot read file data"));
-  header = (void *) readbuf;
-
-  /* Check the header for basic validity.  */
-  if (__builtin_expect (!VALID_ELF_HEADER (header->e_ident, expected, EI_PAD),
-			0))
-    {
-      /* Something is wrong.  */
-      if (*(Elf32_Word *) &header->e_ident !=
-#if BYTE_ORDER == LITTLE_ENDIAN
-	  ((ELFMAG0 << (EI_MAG0 * 8)) |
-	   (ELFMAG1 << (EI_MAG1 * 8)) |
-	   (ELFMAG2 << (EI_MAG2 * 8)) |
-	   (ELFMAG3 << (EI_MAG3 * 8)))
-#else
-	  ((ELFMAG0 << (EI_MAG3 * 8)) |
-	   (ELFMAG1 << (EI_MAG2 * 8)) |
-	   (ELFMAG2 << (EI_MAG1 * 8)) |
-	   (ELFMAG3 << (EI_MAG0 * 8)))
-#endif
-	  )
-	LOSE (0, N_("invalid ELF header"));
-      if (header->e_ident[EI_CLASS] != ELFW(CLASS))
-	{
-	  if (__ELF_NATIVE_CLASS == 32)
-	    LOSE (0, N_("ELF file class not 32-bit"));
-	  else
-	    LOSE (0, N_("ELF file class not 64-bit"));
-	}
-      if (header->e_ident[EI_DATA] != byteorder)
-	{
-	  if (BYTE_ORDER == BIG_ENDIAN)
-	    LOSE (0, "ELF file data encoding not big-endian");
-	  else
-	    LOSE (0, "ELF file data encoding not little-endian");
-	}
-      if (header->e_ident[EI_VERSION] != EV_CURRENT)
-	LOSE (0, N_("ELF file version ident does not match current one"));
-      /* XXX We should be able so set system specific versions which are
-	 allowed here.  */
-      if (!VALID_ELF_OSABI (header->e_ident[EI_OSABI]))
-	LOSE (0, N_("ELF file OS ABI invalid."));
-      if (!VALID_ELF_ABIVERSION (header->e_ident[EI_ABIVERSION]))
-	LOSE (0, N_("ELF file ABI version invalid."));
-      LOSE (0, N_("internal error"));
-    }
-
-  if (__builtin_expect (header->e_version, EV_CURRENT) != EV_CURRENT)
-    LOSE (0, N_("ELF file version does not match current one"));
-  if (! __builtin_expect (elf_machine_matches_host (header), 1))
-    LOSE (0, N_("ELF file machine architecture does not match"));
-  if (__builtin_expect (header->e_phentsize, sizeof (ElfW(Phdr)))
-      != sizeof (ElfW(Phdr)))
-    LOSE (0, N_("ELF file's phentsize not the expected size"));
+  /* This is the ELF header.  We read it in `open_verify'.  */
+  header = (void *) fbp->buf;
 
 #ifndef MAP_ANON
 # define MAP_ANON 0
@@ -889,8 +830,8 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
   l->l_phnum = header->e_phnum;
 
   maplength = header->e_phnum * sizeof (ElfW(Phdr));
-  if (header->e_phoff + maplength <= readlength)
-    phdr = (void *) (readbuf + header->e_phoff);
+  if (header->e_phoff + maplength <= fbp->len)
+    phdr = (void *) (fbp->buf + header->e_phoff);
   else
     {
       phdr = alloca (maplength);
@@ -1254,6 +1195,123 @@ print_search_path (struct r_search_path_elem **list,
     _dl_debug_message (0, "\t\t(", what, ")\n", NULL);
 }
 
+/* Open a file and verify it is an ELF file for this architecture.  We
+   ignore only ELF files for other architectures.  Non-ELF files and
+   ELF files with different header information cause fatal errors since
+   this could mean there is something wrong in the installation and the
+   user might want to know about this.  */
+static int
+open_verify (const char *name, struct filebuf *fbp)
+{
+  /* This is the expected ELF header.  */
+#define ELF32_CLASS ELFCLASS32
+#define ELF64_CLASS ELFCLASS64
+#ifndef VALID_ELF_HEADER
+# define VALID_ELF_HEADER(hdr,exp,size)	(memcmp (hdr, exp, size) == 0)
+# define VALID_ELF_OSABI(osabi)		(osabi == ELFOSABI_SYSV)
+# define VALID_ELF_ABIVERSION(ver)	(ver == 0)
+#endif
+  static const unsigned char expected[EI_PAD] =
+  {
+    [EI_MAG0] = ELFMAG0,
+    [EI_MAG1] = ELFMAG1,
+    [EI_MAG2] = ELFMAG2,
+    [EI_MAG3] = ELFMAG3,
+    [EI_CLASS] = ELFW(CLASS),
+    [EI_DATA] = byteorder,
+    [EI_VERSION] = EV_CURRENT,
+    [EI_OSABI] = ELFOSABI_SYSV,
+    [EI_ABIVERSION] = 0
+  };
+  int fd;
+
+  /* Open the file.  We always open files read-only.  */
+  fd = __open (name, O_RDONLY);
+  if (fd != -1)
+    {
+      ElfW(Ehdr) *ehdr;
+
+      /* We successfully openened the file.  Now verify it is a file
+	 we can use.  */
+      __set_errno (0);
+      fbp->len = __libc_read (fd, fbp->buf, sizeof (fbp->buf));
+
+      /* This is where the ELF header is loaded.  */
+      assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr)));
+      ehdr = (ElfW(Ehdr) *) fbp->buf;
+
+      /* Now run the tests.  */
+      if (__builtin_expect (fbp->len < (ssize_t) sizeof (ElfW(Ehdr)), 0))
+	lose (errno, fd, name, NULL, NULL,
+	      errno == 0 ? N_("file too short") : N_("cannot read file data"));
+
+      /* See whether the ELF header is what we expect.  */
+      if (__builtin_expect (! VALID_ELF_HEADER (ehdr->e_ident, expected,
+						EI_PAD), 0))
+	{
+	  /* Something is wrong.  */
+	  if (*(Elf32_Word *) &ehdr->e_ident !=
+#if BYTE_ORDER == LITTLE_ENDIAN
+	      ((ELFMAG0 << (EI_MAG0 * 8)) |
+	       (ELFMAG1 << (EI_MAG1 * 8)) |
+	       (ELFMAG2 << (EI_MAG2 * 8)) |
+	       (ELFMAG3 << (EI_MAG3 * 8)))
+#else
+	      ((ELFMAG0 << (EI_MAG3 * 8)) |
+	       (ELFMAG1 << (EI_MAG2 * 8)) |
+	       (ELFMAG2 << (EI_MAG1 * 8)) |
+	       (ELFMAG3 << (EI_MAG0 * 8)))
+#endif
+	      )
+	    lose (0, fd, name, NULL, NULL, N_("invalid ELF header"));
+
+	  if (ehdr->e_ident[EI_CLASS] != ELFW(CLASS))
+	    /* This is not a fatal error.  On architectures where
+	       32-bit and 64-bit binaries can be run this might
+	       happen.  */
+	    goto close_and_out;
+
+	  if (ehdr->e_ident[EI_DATA] != byteorder)
+	    {
+	      if (BYTE_ORDER == BIG_ENDIAN)
+		lose (0, fd, name, NULL, NULL,
+		      "ELF file data encoding not big-endian");
+	      else
+		lose (0, fd, name, NULL, NULL,
+		      "ELF file data encoding not little-endian");
+	    }
+	  if (ehdr->e_ident[EI_VERSION] != EV_CURRENT)
+	    lose (0, fd, name, NULL, NULL,
+		  N_("ELF file version ident does not match current one"));
+	  /* XXX We should be able so set system specific versions which are
+	     allowed here.  */
+	  if (!VALID_ELF_OSABI (ehdr->e_ident[EI_OSABI]))
+	    lose (0, fd, name, NULL, NULL, N_("ELF file OS ABI invalid."));
+	  if (!VALID_ELF_ABIVERSION (ehdr->e_ident[EI_ABIVERSION]))
+	    lose (0, fd, name, NULL, NULL,
+		  N_("ELF file ABI version invalid."));
+	  lose (0, fd, name, NULL, NULL, N_("internal error"));
+	}
+
+      if (__builtin_expect (ehdr->e_version, EV_CURRENT) != EV_CURRENT)
+	lose (0, fd, name, NULL, NULL,
+	      N_("ELF file version does not match current one"));
+      if (! __builtin_expect (elf_machine_matches_host (ehdr), 1))
+	{
+	close_and_out:
+	  __close (fd);
+	  __set_errno (ENOENT);
+	  fd = -1;
+	}
+      else if (__builtin_expect (ehdr->e_phentsize, sizeof (ElfW(Phdr)))
+	       != sizeof (ElfW(Phdr)))
+	lose (0, fd, name, NULL, NULL,
+	      N_("ELF file's phentsize not the expected size"));
+    }
+
+  return fd;
+}
+
 /* Try to open NAME in one of the directories in *DIRSP.
    Return the fd, or -1.  If successful, fill in *REALNAME
    with the malloc'd full directory name.  If it turns out
@@ -1263,7 +1321,8 @@ print_search_path (struct r_search_path_elem **list,
 
 static int
 open_path (const char *name, size_t namelen, int preloaded,
-	   struct r_search_path_struct *sps, char **realname)
+	   struct r_search_path_struct *sps, char **realname,
+	   struct filebuf *fbp)
 {
   struct r_search_path_elem **dirs = sps->dirs;
   char *buf;
@@ -1305,7 +1364,7 @@ open_path (const char *name, size_t namelen, int preloaded,
 	  if (__builtin_expect (_dl_debug_libs, 0))
 	    _dl_debug_message (1, "  trying file=", buf, "\n", NULL);
 
-	  fd = __open (buf, O_RDONLY);
+	  fd = open_verify (buf, fbp);
 	  if (this_dir->status[cnt] == unknown)
 	    {
 	      if (fd != -1)
@@ -1398,6 +1457,7 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded,
   char *realname;
   char *name_copy;
   struct link_map *l;
+  struct filebuf fb;
 
   /* Look for this name among those already loaded.  */
   for (l = _dl_loaded; l; l = l->l_next)
@@ -1475,12 +1535,12 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded,
 
 		      if (l->l_rpath_dirs.dirs != (void *) -1)
 			fd = open_path (name, namelen, preloaded,
-					&l->l_rpath_dirs, &realname);
+					&l->l_rpath_dirs, &realname, &fb);
 		    }
 		}
 	      else if (l->l_rpath_dirs.dirs != (void *) -1)
 		fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs,
-				&realname);
+				&realname, &fb);
 	    }
 
 	  /* If dynamically linked, try the DT_RPATH of the executable
@@ -1489,13 +1549,13 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded,
 	  if (fd == -1 && l && l->l_type != lt_loaded && l != loader
 	      && l->l_rpath_dirs.dirs != (void *) -1)
 	    fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs,
-			    &realname);
+			    &realname, &fb);
 	}
 
       /* Try the LD_LIBRARY_PATH environment variable.  */
       if (fd == -1 && env_path_list.dirs != (void *) -1)
 	fd = open_path (name, namelen, preloaded, &env_path_list,
-			&realname);
+			&realname, &fb);
 
       /* Look at the RUNPATH informaiton for this binary.  */
       if (loader != NULL && loader->l_runpath_dirs.dirs != (void *) -1)
@@ -1515,12 +1575,12 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded,
 
 		  if (loader->l_runpath_dirs.dirs != (void *) -1)
 		    fd = open_path (name, namelen, preloaded,
-				    &loader->l_runpath_dirs, &realname);
+				    &loader->l_runpath_dirs, &realname, &fb);
 		}
 	    }
 	  else if (loader->l_runpath_dirs.dirs != (void *) -1)
 	    fd = open_path (name, namelen, preloaded,
-			    &loader->l_runpath_dirs, &realname);
+			    &loader->l_runpath_dirs, &realname, &fb);
 	}
 
       if (fd == -1)
@@ -1562,7 +1622,7 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded,
 
 	      if (cached)
 		{
-		  fd = __open (cached, O_RDONLY);
+		  fd = open_verify (cached, &fb);
 		  if (fd != -1)
 		    {
 		      realname = local_strdup (cached);
@@ -1582,7 +1642,7 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded,
 	      __builtin_expect (!(l->l_flags_1 & DF_1_NODEFLIB), 1))
 	  && rtld_search_dirs.dirs != (void *) -1)
 	fd = open_path (name, namelen, preloaded, &rtld_search_dirs,
-			&realname);
+			&realname, &fb);
 
       /* Add another newline when we a tracing the library loading.  */
       if (__builtin_expect (_dl_debug_libs, 0))
@@ -1598,7 +1658,7 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded,
 	fd = -1;
       else
 	{
-	  fd = __open (realname, O_RDONLY);
+	  fd = open_verify (realname, &fb);
 	  if (fd == -1)
 	    free (realname);
 	}
@@ -1634,5 +1694,5 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded,
 	_dl_signal_error (errno, name, N_("cannot open shared object file"));
     }
 
-  return _dl_map_object_from_fd (name, fd, realname, loader, type, mode);
+  return _dl_map_object_from_fd (name, fd, &fb, realname, loader, type, mode);
 }