[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)