read_line_header parses the DWARF line program header but never checks
the header_length field against the bounds of the unit.  read_srcfiles
then does

  lineendp = lh->header_start + lh->header_length;

and uses that pointer as the upper bound while scanning the directory and
file-name tables (and read_srclines similarly computes
lh->header_start + lh->header_length as the start of the line program).

A .debug_line unit whose header_length is larger than the remaining unit
data therefore makes lineendp point past the end of the section, and the
directory scan reads out of bounds:

  ERROR: AddressSanitizer: heap-buffer-overflow ... READ of size 2
    #0 __interceptor_memchr
    #1 read_srcfiles libdw/dwarf_getsrclines.c:373
    #2 get_lines_or_files libdw/dwarf_getsrclines.c:1353
    #3 __libdw_getsrcfiles libdw/dwarf_getsrclines.c:1425
    #4 dwarf_getsrcfiles libdw/dwarf_getsrcfiles.c:105
  0 bytes to the right of 17-byte region

header_length counts the bytes from directly after the field to the
start of the line number program, so it must fit within the unit (which
read_line_header has already bounded to the section via unit_length).
Reject a header_length that exceeds the remaining unit bytes, turning the
crafted input into a clean DWARF_E_INVALID_DEBUG_LINE error.  Valid
DWARF is unaffected.

Signed-off-by: Sayed Kaif <[email protected]>
---
diff --git a/libdw/dwarf_getsrclines.c b/libdw/dwarf_getsrclines.c
index 3521511..1d5f659 100644
--- a/libdw/dwarf_getsrclines.c
+++ b/libdw/dwarf_getsrclines.c
@@ -246,6 +246,15 @@ read_line_header (Dwarf *dbg, unsigned address_size,
     }
   lh->header_start = linep;
 
+  /* The header length is the number of bytes from here to the start of
+     the line number program, so it must stay within the unit (and thus
+     the section).  Without this check a bogus header_length makes
+     read_srcfiles/read_srclines compute an out-of-bounds end pointer
+     (header_start + header_length) and read past the section while
+     parsing the directory and file tables.  */
+  if (unlikely (lh->header_length > (size_t) (lineendp - linep)))
+    goto invalid_data;
+
   /* Next the minimum instruction length.  */
   if (unlikely ((size_t) (lineendp - linep) < 1))
     goto invalid_data;

Reply via email to