[PATCH 5/6] robustify: libelf

Mark Wielaard mjw at redhat.com
Fri Jan 17 20:20:43 UTC 2014


From: Jakub Jelinek <jakub at redhat.com>

Signed-off-by: Mark Wielaard <mjw at redhat.com>
---
 libelf/ChangeLog           |   20 ++++++++++++++++++++
 libelf/elf32_getphdr.c     |   12 +++++++++++-
 libelf/elf32_getshdr.c     |   15 +++++++++++++--
 libelf/elf32_newphdr.c     |    8 +++++++-
 libelf/elf32_updatefile.c  |    9 ++++++++-
 libelf/elf_begin.c         |   32 +++++++++++++++++++++++++++++---
 libelf/elf_getarsym.c      |    5 ++++-
 libelf/elf_getshdrstrndx.c |   44 +++++++++++++++++++++++++++++++++++++-------
 libelf/elf_newscn.c        |   12 ++++++++++--
 9 files changed, 139 insertions(+), 18 deletions(-)

diff --git a/libelf/ChangeLog b/libelf/ChangeLog
index 148c945..ee070e0 100644
--- a/libelf/ChangeLog
+++ b/libelf/ChangeLog
@@ -1,3 +1,23 @@
+2014-01-17  Jakub Jelinek  <jakub at redhat.com>
+
+	* elf32_getphdr.c (elfw2(LIBELFBITS,getphdr)): Check if program header
+	table fits into object's bounds.
+	* elf_getshdrstrndx.c (elf_getshstrndx): Add elf->start_offset to
+	elf->map_address.  Check if first section header fits into object's
+	bounds.
+	* elf32_getshdr.c (elfw2(LIBELFBITS,getshdr)):
+	Check if section header table fits into object's bounds.
+	* elf_begin.c (get_shnum): Ensure section headers fits into
+	object's bounds.
+	(file_read_elf): Make sure scncnt is small enough to allocate both
+	ElfXX_Shdr and Elf_Scn array.  Make sure section and program header
+	tables fit into object's bounds.  Avoid memory leak on failure.
+	* elf_newscn.c (elf_newscn): Check for overflow.
+	* elf32_updatefile.c (__elfw2(LIBELFBITS,updatemmap)): Likewise.
+	(__elfw2(LIBELFBITS,updatefile)): Likewise.
+	* elf32_newphdr.c (elfw2(LIBELFBITS,newphdr)): Likewise.
+	* elf_getarsym.c (elf_getarsym): Likewise.
+
 2013-11-08  Mark Wielaard  <mjw at redhat.com>
 
 	* elf32_updatefile.c (elfXX_updatemmap): Only memcpy ehdr when not
diff --git a/libelf/elf32_getphdr.c b/libelf/elf32_getphdr.c
index a47c9f7..e74e63f 100644
--- a/libelf/elf32_getphdr.c
+++ b/libelf/elf32_getphdr.c
@@ -1,5 +1,5 @@
 /* Get ELF program header table.
-   Copyright (C) 1998-2010 Red Hat, Inc.
+   Copyright (C) 1998-2010, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper at redhat.com>, 1998.
 
@@ -93,6 +93,16 @@ __elfw2(LIBELFBITS,getphdr_wrlock) (elf)
 
       if (elf->map_address != NULL)
 	{
+	  /* First see whether the information in the ELF header is
+	     valid and it does not ask for too much.  */
+	  if (unlikely (ehdr->e_phoff >= elf->maximum_size)
+	      || unlikely (elf->maximum_size - ehdr->e_phoff < size))
+	    {
+	      /* Something is wrong.  */
+	      __libelf_seterrno (ELF_E_INVALID_PHDR);
+	      goto out;
+	    }
+
 	  /* All the data is already mapped.  Use it.  */
 	  void *file_phdr = ((char *) elf->map_address
 			     + elf->start_offset + ehdr->e_phoff);
diff --git a/libelf/elf32_getshdr.c b/libelf/elf32_getshdr.c
index bd9340d..7417047 100644
--- a/libelf/elf32_getshdr.c
+++ b/libelf/elf32_getshdr.c
@@ -1,5 +1,5 @@
 /* Return section header.
-   Copyright (C) 1998-2002, 2005, 2007, 2009, 2012 Red Hat, Inc.
+   Copyright (C) 1998-2002, 2005, 2007, 2009, 2012, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper at redhat.com>, 1998.
 
@@ -60,7 +60,8 @@ load_shdr_wrlock (Elf_Scn *scn)
     goto out;
 
   size_t shnum;
-  if (__elf_getshdrnum_rdlock (elf, &shnum) != 0)
+  if (__elf_getshdrnum_rdlock (elf, &shnum) != 0
+      || shnum > SIZE_MAX / sizeof (ElfW2(LIBELFBITS,Shdr)))
     goto out;
   size_t size = shnum * sizeof (ElfW2(LIBELFBITS,Shdr));
 
@@ -77,6 +78,16 @@ load_shdr_wrlock (Elf_Scn *scn)
 
   if (elf->map_address != NULL)
     {
+      /* First see whether the information in the ELF header is
+	 valid and it does not ask for too much.  */
+      if (unlikely (ehdr->e_shoff >= elf->maximum_size)
+	  || unlikely (elf->maximum_size - ehdr->e_shoff < size))
+	{
+	  /* Something is wrong.  */
+	  __libelf_seterrno (ELF_E_INVALID_SECTION_HEADER);
+	  goto free_and_out;
+	}
+
       ElfW2(LIBELFBITS,Shdr) *notcvt;
 
       /* All the data is already mapped.  If we could use it
diff --git a/libelf/elf32_newphdr.c b/libelf/elf32_newphdr.c
index 1db2080..01038e7 100644
--- a/libelf/elf32_newphdr.c
+++ b/libelf/elf32_newphdr.c
@@ -1,5 +1,5 @@
 /* Create new ELF program header table.
-   Copyright (C) 1999-2010 Red Hat, Inc.
+   Copyright (C) 1999-2010, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper at redhat.com>, 1998.
 
@@ -114,6 +114,12 @@ elfw2(LIBELFBITS,newphdr) (elf, count)
 	   || count == PN_XNUM
 	   || elf->state.ELFW(elf,LIBELFBITS).phdr == NULL)
     {
+      if (unlikely (count > SIZE_MAX / sizeof (ElfW2(LIBELFBITS,Phdr))))
+	{
+	  result = NULL;
+	  goto out;
+	}
+
       /* Allocate a new program header with the appropriate number of
 	 elements.  */
       result = (ElfW2(LIBELFBITS,Phdr) *)
diff --git a/libelf/elf32_updatefile.c b/libelf/elf32_updatefile.c
index b39e284..153e377 100644
--- a/libelf/elf32_updatefile.c
+++ b/libelf/elf32_updatefile.c
@@ -1,5 +1,5 @@
 /* Write changed data structures.
-   Copyright (C) 2000-2010 Red Hat, Inc.
+   Copyright (C) 2000-2010, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper at redhat.com>, 2000.
 
@@ -202,6 +202,9 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
   /* Write all the sections.  Well, only those which are modified.  */
   if (shnum > 0)
     {
+      if (unlikely (shnum > SIZE_MAX / sizeof (Elf_Scn *)))
+	return 1;
+
       Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns;
       Elf_Scn **scns = (Elf_Scn **) alloca (shnum * sizeof (Elf_Scn *));
       char *const shdr_start = ((char *) elf->map_address + elf->start_offset
@@ -624,6 +627,10 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
   /* Write all the sections.  Well, only those which are modified.  */
   if (shnum > 0)
     {
+      if (unlikely (shnum > SIZE_MAX / (sizeof (Elf_Scn *)
+					+ sizeof (ElfW2(LIBELFBITS,Shdr)))))
+	return 1;
+
       off_t shdr_offset = elf->start_offset + ehdr->e_shoff;
 #if EV_NUM != 2
       xfct_t shdr_fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR];
diff --git a/libelf/elf_begin.c b/libelf/elf_begin.c
index b9d5cea..a592fbf 100644
--- a/libelf/elf_begin.c
+++ b/libelf/elf_begin.c
@@ -1,5 +1,5 @@
 /* Create descriptor for processing file.
-   Copyright (C) 1998-2010, 2012 Red Hat, Inc.
+   Copyright (C) 1998-2010, 2012, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper at redhat.com>, 1998.
 
@@ -144,7 +144,8 @@ get_shnum (void *map_address, unsigned char *e_ident, int fildes, off_t offset,
 
       if (unlikely (result == 0) && ehdr.e32->e_shoff != 0)
 	{
-	  if (ehdr.e32->e_shoff + sizeof (Elf32_Shdr) > maxsize)
+	  if (unlikely (ehdr.e32->e_shoff >= maxsize)
+	      || unlikely (maxsize - ehdr.e32->e_shoff < sizeof (Elf32_Shdr)))
 	    /* Cannot read the first section header.  */
 	    return 0;
 
@@ -192,7 +193,8 @@ get_shnum (void *map_address, unsigned char *e_ident, int fildes, off_t offset,
 
       if (unlikely (result == 0) && ehdr.e64->e_shoff != 0)
 	{
-	  if (ehdr.e64->e_shoff + sizeof (Elf64_Shdr) > maxsize)
+	  if (unlikely (ehdr.e64->e_shoff >= maxsize)
+	      || unlikely (ehdr.e64->e_shoff + sizeof (Elf64_Shdr) > maxsize))
 	    /* Cannot read the first section header.  */
 	    return 0;
 
@@ -264,6 +266,15 @@ file_read_elf (int fildes, void *map_address, unsigned char *e_ident,
     /* Could not determine the number of sections.  */
     return NULL;
 
+  /* Check for too many sections.  */
+  if (e_ident[EI_CLASS] == ELFCLASS32)
+    {
+      if (scncnt > SIZE_MAX / (sizeof (Elf_Scn) + sizeof (Elf32_Shdr)))
+	return NULL;
+    }
+  else if (scncnt > SIZE_MAX / (sizeof (Elf_Scn) + sizeof (Elf64_Shdr)))
+    return NULL;
+
   /* We can now allocate the memory.  Even if there are no section headers,
      we allocate space for a zeroth section in case we need it later.  */
   const size_t scnmax = (scncnt ?: (cmd == ELF_C_RDWR || cmd == ELF_C_RDWR_MMAP)
@@ -303,6 +314,16 @@ file_read_elf (int fildes, void *map_address, unsigned char *e_ident,
 	{
 	  /* We can use the mmapped memory.  */
 	  elf->state.elf32.ehdr = ehdr;
+
+	  if (unlikely (ehdr->e_shoff >= maxsize)
+	      || unlikely (maxsize - ehdr->e_shoff
+			   < scncnt * sizeof (Elf32_Shdr)))
+	    {
+	    free_and_out:
+	      free (elf);
+	      __libelf_seterrno (ELF_E_INVALID_FILE);
+	      return NULL;
+	    }
 	  elf->state.elf32.shdr
 	    = (Elf32_Shdr *) ((char *) ehdr + ehdr->e_shoff);
 
@@ -389,6 +410,11 @@ file_read_elf (int fildes, void *map_address, unsigned char *e_ident,
 	{
 	  /* We can use the mmapped memory.  */
 	  elf->state.elf64.ehdr = ehdr;
+
+	  if (unlikely (ehdr->e_shoff >= maxsize)
+	      || unlikely (ehdr->e_shoff
+			   + scncnt * sizeof (Elf32_Shdr) > maxsize))
+	    goto free_and_out;
 	  elf->state.elf64.shdr
 	    = (Elf64_Shdr *) ((char *) ehdr + ehdr->e_shoff);
 
diff --git a/libelf/elf_getarsym.c b/libelf/elf_getarsym.c
index d0bb28a..7325190 100644
--- a/libelf/elf_getarsym.c
+++ b/libelf/elf_getarsym.c
@@ -1,5 +1,5 @@
 /* Return symbol table of archive.
-   Copyright (C) 1998-2000, 2002, 2005, 2012 Red Hat, Inc.
+   Copyright (C) 1998-2000, 2002, 2005, 2009, 2012, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper at redhat.com>, 1998.
 
@@ -183,6 +183,9 @@ elf_getarsym (elf, ptr)
       size_t index_size = atol (tmpbuf);
 
       if (SARMAG + sizeof (struct ar_hdr) + index_size > elf->maximum_size
+#if SIZE_MAX <= 4294967295U
+	  || n >= SIZE_MAX / sizeof (Elf_Arsym)
+#endif
 	  || n * w > index_size)
 	{
 	  /* This index table cannot be right since it does not fit into
diff --git a/libelf/elf_getshdrstrndx.c b/libelf/elf_getshdrstrndx.c
index 57ad800..1dbed4c 100644
--- a/libelf/elf_getshdrstrndx.c
+++ b/libelf/elf_getshdrstrndx.c
@@ -1,5 +1,5 @@
 /* Return section index of section header string table.
-   Copyright (C) 2002, 2005, 2009 Red Hat, Inc.
+   Copyright (C) 2002, 2005, 2009, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper at redhat.com>, 2002.
 
@@ -104,10 +104,25 @@ elf_getshdrstrndx (elf, dst)
 	      if (elf->map_address != NULL
 		  && elf->state.elf32.ehdr->e_ident[EI_DATA] == MY_ELFDATA
 		  && (ALLOW_UNALIGNED
-		      || (((size_t) ((char *) elf->map_address + offset))
+		      || (((size_t) ((char *) elf->map_address
+			   + elf->start_offset + offset))
 			  & (__alignof__ (Elf32_Shdr) - 1)) == 0))
-		/* We can directly access the memory.  */
-		num = ((Elf32_Shdr *) (elf->map_address + offset))->sh_link;
+		{
+		  /* First see whether the information in the ELF header is
+		     valid and it does not ask for too much.  */
+		  if (unlikely (elf->maximum_size - offset
+				< sizeof (Elf32_Shdr)))
+		    {
+		      /* Something is wrong.  */
+		      __libelf_seterrno (ELF_E_INVALID_SECTION_HEADER);
+		      result = -1;
+		      goto out;
+		    }
+
+		  /* We can directly access the memory.  */
+		  num = ((Elf32_Shdr *) (elf->map_address + elf->start_offset
+					 + offset))->sh_link;
+		}
 	      else
 		{
 		  /* We avoid reading in all the section headers.  Just read
@@ -142,10 +157,25 @@ elf_getshdrstrndx (elf, dst)
 	      if (elf->map_address != NULL
 		  && elf->state.elf64.ehdr->e_ident[EI_DATA] == MY_ELFDATA
 		  && (ALLOW_UNALIGNED
-		      || (((size_t) ((char *) elf->map_address + offset))
+		      || (((size_t) ((char *) elf->map_address
+			   + elf->start_offset + offset))
 			  & (__alignof__ (Elf64_Shdr) - 1)) == 0))
-		/* We can directly access the memory.  */
-		num = ((Elf64_Shdr *) (elf->map_address + offset))->sh_link;
+		{
+		  /* First see whether the information in the ELF header is
+		     valid and it does not ask for too much.  */
+		  if (unlikely (elf->maximum_size - offset
+				< sizeof (Elf64_Shdr)))
+		    {
+		      /* Something is wrong.  */
+		      __libelf_seterrno (ELF_E_INVALID_SECTION_HEADER);
+		      result = -1;
+		      goto out;
+		    }
+
+		  /* We can directly access the memory.  */
+		  num = ((Elf64_Shdr *) (elf->map_address + elf->start_offset
+					 + offset))->sh_link;
+		}
 	      else
 		{
 		  /* We avoid reading in all the section headers.  Just read
diff --git a/libelf/elf_newscn.c b/libelf/elf_newscn.c
index 70d29b6..6e0029e 100644
--- a/libelf/elf_newscn.c
+++ b/libelf/elf_newscn.c
@@ -1,5 +1,5 @@
 /* Append new section.
-   Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005, 2009, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper at redhat.com>, 1998.
 
@@ -83,10 +83,18 @@ elf_newscn (elf)
   else
     {
       /* We must allocate a new element.  */
-      Elf_ScnList *newp;
+      Elf_ScnList *newp = NULL;
 
       assert (elf->state.elf.scnincr > 0);
 
+      if (
+#if SIZE_MAX <= 4294967295U
+	  likely (elf->state.elf.scnincr
+		  < SIZE_MAX / 2 / sizeof (Elf_Scn) - sizeof (Elf_ScnList))
+#else
+	  1
+#endif
+	  )
       newp = (Elf_ScnList *) calloc (sizeof (Elf_ScnList)
 				     + ((elf->state.elf.scnincr *= 2)
 					* sizeof (Elf_Scn)), 1);
-- 
1.7.1



More information about the elfutils-devel mailing list