[binutils-gdb] DWARF1 AT_sibling sanity check
Alan Modra via Binutils-cvs <[email protected]> Mon, 27 Jul 2026 04:54:17 +0000 (GMT)
| Newsgroups | gmane.comp.gnu.binutils.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=3Dbinutils-gdb.git;h=3D6867ffe72744= c0bf4309c51e04d1f6abad32411b commit 6867ffe72744c0bf4309c51e04d1f6abad32411b Author: Alan Modra <[email protected]> Date: Mon Jul 27 11:27:45 2026 +0930 DWARF1 AT_sibling sanity check =20 I am not absolutely certain that DWARF version 1 AT_sibling always points forward, but that seems to be the case for gcc-3.3 from a quick look at gcc/dwarfout.c and examining some i686-linux output. I believe gcc stopped supporting DWARF version 1 after gcc-3.3. =20 If backward links are allowed then it is considerably more tedious to protect against fuzzed object files that loop forever reading DWARF1. So this patch may break addr2line and objdump -dS for some old files. If it does, well, removing DWARF1 support entirely would break that support too. (readelf doesn't support DWARF version 1). =20 * dwarf1.c (parse_die): Replace abfd and aDiePtrEnd parameters with stash pointer. Adjust to suit. Sanity check AT_sibling value. (parse_functions_in_unit, _bfd_dwarf1_find_nearest_line): Adjust parse_die calls. Diff: --- bfd/dwarf1.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/bfd/dwarf1.c b/bfd/dwarf1.c index b899cc7876f..2a34b88765b 100644 --- a/bfd/dwarf1.c +++ b/bfd/dwarf1.c @@ -178,23 +178,24 @@ alloc_dwarf1_func (struct dwarf1_debug* stash, struct= dwarf1_unit* aUnit) Return FALSE if the die is invalidly formatted; TRUE otherwise. */ =20 static bool -parse_die (bfd * abfd, - struct die_info * aDieInfo, - bfd_byte * aDiePtr, - bfd_byte * aDiePtrEnd) +parse_die (const struct dwarf1_debug *stash, + struct die_info *aDieInfo, + bfd_byte *aDiePtr) { + bfd *abfd =3D stash->abfd; + bfd_byte *aDiePtrEnd; bfd_byte *this_die =3D aDiePtr; bfd_byte *xptr =3D this_die; =20 memset (aDieInfo, 0, sizeof (* aDieInfo)); =20 /* First comes the length. */ - if (xptr + 4 > aDiePtrEnd) + if (xptr + 4 > stash->debug_section_end) return false; aDieInfo->length =3D bfd_get_32 (abfd, xptr); xptr +=3D 4; if (aDieInfo->length <=3D 4 - || (size_t) (aDiePtrEnd - this_die) < aDieInfo->length) + || (size_t) (stash->debug_section_end - this_die) < aDieInfo->length) return false; aDiePtrEnd =3D this_die + aDieInfo->length; if (aDieInfo->length < 6) @@ -232,7 +233,21 @@ parse_die (bfd * abfd, if (xptr + 4 <=3D aDiePtrEnd) { if (attr =3D=3D AT_sibling) - aDieInfo->sibling =3D bfd_get_32 (abfd, xptr); + { + aDieInfo->sibling =3D bfd_get_32 (abfd, xptr); + if (aDieInfo->sibling !=3D 0) + { + /* Reject a sibling earlier than the current die, + or past the end of the .debug section. This + is to stop fuzzers generating endless loops. */ + size_t next_off =3D aDiePtrEnd - stash->debug_section; + size_t sec_size =3D (stash->debug_section_end + - stash->debug_section); + if (aDieInfo->sibling < next_off + || aDieInfo->sibling > sec_size) + return false; + } + } else if (attr =3D=3D AT_stmt_list) { aDieInfo->stmt_list_offset =3D bfd_get_32 (abfd, xptr); @@ -384,8 +399,7 @@ parse_functions_in_unit (struct dwarf1_debug* stash, st= ruct dwarf1_unit* aUnit) { struct die_info eachDieInfo; =20 - if (! parse_die (stash->abfd, &eachDieInfo, eachDie, - stash->debug_section_end)) + if (!parse_die (stash, &eachDieInfo, eachDie)) return false; =20 if (eachDieInfo.tag =3D=3D TAG_global_subroutine @@ -550,8 +564,7 @@ _bfd_dwarf1_find_nearest_line (bfd *abfd, { struct die_info aDieInfo; =20 - if (! parse_die (stash->abfd, &aDieInfo, stash->currentDie, - stash->debug_section_end)) + if (!parse_die (stash, &aDieInfo, stash->currentDie)) return false; =20 if (aDieInfo.tag =3D=3D TAG_compile_unit)