From 2562759d6fe5b364fe224852e64e8bda39eb2e35 Mon Sep 17 00:00:00 2001
From: Mark Wielaard <mark@klomp.org>
Date: Sun, 20 Jan 2019 22:10:18 +0100
Subject: [PATCH] libdw: Check terminating NUL byte in dwarf_getsrclines for
 dir/file table.

For DWARF version < 5 the .debug_line directory and file tables consist
of a terminating NUL byte after all strings. The code used to just skip
this without checking it actually existed. This could case a spurious
read past the end of data.

Fix the same issue in readelf.

https://sourceware.org/bugzilla/show_bug.cgi?id=24102

Signed-off-by: Mark Wielaard <mark@klomp.org>

Upstream-Status: Backport
CVE: CVE-2019-7149
Signed-off-by: Armin Kuster <akuster@mvista.com>

---
 libdw/ChangeLog           |  5 +++++
 libdw/dwarf_getsrclines.c | 11 ++++++++---
 src/ChangeLog             |  5 +++++
 src/readelf.c             |  8 ++++++--
 4 files changed, 24 insertions(+), 5 deletions(-)

Index: elfutils-0.175/libdw/dwarf_getsrclines.c
===================================================================
--- elfutils-0.175.orig/libdw/dwarf_getsrclines.c
+++ elfutils-0.175/libdw/dwarf_getsrclines.c
@@ -315,7 +315,7 @@ read_srclines (Dwarf *dbg,
   if (version < 5)
     {
       const unsigned char *dirp = linep;
-      while (*dirp != 0)
+      while (dirp < lineendp && *dirp != 0)
 	{
 	  uint8_t *endp = memchr (dirp, '\0', lineendp - dirp);
 	  if (endp == NULL)
@@ -323,6 +323,8 @@ read_srclines (Dwarf *dbg,
 	  ++ndirs;
 	  dirp = endp + 1;
 	}
+      if (dirp >= lineendp || *dirp != '\0')
+	goto invalid_data;
       ndirs = ndirs + 1; /* There is always the "unknown" dir.  */
     }
   else
@@ -392,11 +394,12 @@ read_srclines (Dwarf *dbg,
 	{
 	  dirarray[n].dir = (char *) linep;
 	  uint8_t *endp = memchr (linep, '\0', lineendp - linep);
-	  assert (endp != NULL);
+	  assert (endp != NULL); // Checked above when calculating ndirlist.
 	  dirarray[n].len = endp - linep;
 	  linep = endp + 1;
 	}
       /* Skip the final NUL byte.  */
+      assert (*linep == '\0'); // Checked above when calculating ndirlist.
       ++linep;
     }
   else
@@ -471,7 +474,7 @@ read_srclines (Dwarf *dbg,
     {
       if (unlikely (linep >= lineendp))
 	goto invalid_data;
-      while (*linep != 0)
+      while (linep < lineendp && *linep != '\0')
 	{
 	  struct filelist *new_file = NEW_FILE ();
 
@@ -527,6 +530,8 @@ read_srclines (Dwarf *dbg,
 	    goto invalid_data;
 	  get_uleb128 (new_file->info.length, linep, lineendp);
 	}
+      if (linep >= lineendp || *linep != '\0')
+	goto invalid_data;
       /* Skip the final NUL byte.  */
       ++linep;
     }
Index: elfutils-0.175/src/readelf.c
===================================================================
--- elfutils-0.175.orig/src/readelf.c
+++ elfutils-0.175/src/readelf.c
@@ -8444,7 +8444,7 @@ print_debug_line_section (Dwfl_Module *d
 	}
       else
 	{
-	  while (*linep != 0)
+	  while (linep < lineendp && *linep != 0)
 	    {
 	      unsigned char *endp = memchr (linep, '\0', lineendp - linep);
 	      if (unlikely (endp == NULL))
@@ -8454,6 +8454,8 @@ print_debug_line_section (Dwfl_Module *d
 
 	      linep = endp + 1;
 	    }
+	  if (linep >= lineendp || *linep != 0)
+	    goto invalid_unit;
 	  /* Skip the final NUL byte.  */
 	  ++linep;
 	}
@@ -8523,7 +8525,7 @@ print_debug_line_section (Dwfl_Module *d
       else
 	{
 	  puts (gettext (" Entry Dir   Time      Size      Name"));
-	  for (unsigned int cnt = 1; *linep != 0; ++cnt)
+	  for (unsigned int cnt = 1; linep < lineendp && *linep != 0; ++cnt)
 	    {
 	      /* First comes the file name.  */
 	      char *fname = (char *) linep;
@@ -8553,6 +8555,8 @@ print_debug_line_section (Dwfl_Module *d
 	      printf (" %-5u %-5u %-9u %-9u %s\n",
 		      cnt, diridx, mtime, fsize, fname);
 	    }
+	  if (linep >= lineendp || *linep != '\0')
+	    goto invalid_unit;
 	  /* Skip the final NUL byte.  */
 	  ++linep;
 	}
Index: elfutils-0.175/libdw/ChangeLog
===================================================================
--- elfutils-0.175.orig/libdw/ChangeLog
+++ elfutils-0.175/libdw/ChangeLog
@@ -1,3 +1,8 @@
+2019-01-20  Mark Wielaard  <mark@klomp.org>
+
+       * dwarf_getsrclines.c (read_srclines): Check terminating NUL byte
+       for dir and file lists.
+
 2018-10-20  Mark Wielaard  <mark@klomp.org>
 
 	* libdw.map (ELFUTILS_0.175): New section. Add dwelf_elf_begin.
Index: elfutils-0.175/src/ChangeLog
===================================================================
--- elfutils-0.175.orig/src/ChangeLog
+++ elfutils-0.175/src/ChangeLog
@@ -1,3 +1,8 @@
+2019-01-20  Mark Wielaard  <mark@klomp.org>
+
+       * readelf.c (print_debug_line_section): Check terminating NUL byte
+       for dir and file tables.
+
 2018-11-10  Mark Wielaard  <mark@klomp.org>
 
 	* elflint.c (check_program_header): Allow PT_GNU_EH_FRAME segment
