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;