[binutils-gdb] PR 33914 abort parsing fuzzed .debug_line

Alan Modra amodra@sourceware.org
Mon Feb 23 03:02:29 GMT 2026


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=8c4a97c801634ee89c8b9aa83bdd697c9757d3aa

commit 8c4a97c801634ee89c8b9aa83bdd697c9757d3aa
Author: Alan Modra <amodra@gmail.com>
Date:   Mon Feb 23 11:18:17 2026 +1030

    PR 33914 abort parsing fuzzed .debug_line
    
    The abort is due to an unexpected DW_FORM_addrx3 entry in the
    testcase, which requires pointer_size to be known.
    
            * dwarf.c (address_size_ok): New function.
            (read_debug_line_header): Use address_size_ok.
            (display_formatted_table): Pass li_address_size as
            pointer_size to read_and_display_attr_value.
            (display_debug_lines_decoded): Likewise.
            (display_debug_macro, display_debug_names): Similarly pass
            offset_size.
            (display_loclists_unit_header): Use address_size_ok.
            (display_debug_loc, display_debug_addr): Likewise.
            (display_debug_aranges): Likewise.  Remove power of two check
            on address_size.  Calculate and pad to tuple_size.

Diff:
---
 binutils/dwarf.c | 126 +++++++++++++++++++++++++------------------------------
 1 file changed, 58 insertions(+), 68 deletions(-)

diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index 3be02345afb..42e538805ec 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -5646,6 +5646,24 @@ load_debug_info (void * file)
   return 0;
 }
 
+static bool
+address_size_ok (const char *sec_name, unsigned addr_size, unsigned seg_size)
+{
+  if (seg_size != 0)
+    {
+      warn (_("Unsupported segment selector size (%u) in %s section.\n"),
+	    seg_size, sec_name);
+      return false;
+    }
+  if (addr_size == 0 || addr_size > 8)
+    {
+      warn (_("Unsupported address size (%u) in %s section.\n"),
+	    addr_size, sec_name);
+      return false;
+    }
+  return true;
+}
+
 /* Read a DWARF .debug_line section header starting at DATA.
    Upon success returns an updated DATA pointer and the LINFO
    structure and the END_OF_SEQUENCE pointer will be filled in.
@@ -5717,13 +5735,9 @@ read_debug_line_header (struct dwarf_section * section,
       SAFE_BYTE_GET_AND_INC (linfo->li_address_size, hdrptr, 1, end);
 
       SAFE_BYTE_GET_AND_INC (linfo->li_segment_size, hdrptr, 1, end);
-      if (linfo->li_segment_size != 0)
-	{
-	  warn (_("The %s section contains "
-		  "unsupported segment selector size: %d.\n"),
-		section->name, linfo->li_segment_size);
-	  return NULL;
-	}
+      if (!address_size_ok (section->name, linfo->li_address_size,
+			    linfo->li_segment_size))
+	return NULL;
     }
 
   SAFE_BYTE_GET_AND_INC (linfo->li_prologue_length, hdrptr,
@@ -5858,12 +5872,13 @@ display_formatted_table (unsigned char *data,
 
 	      READ_ULEB (content_type, format, end);
 	      READ_ULEB (form, format, end);
+	      bool do_loc = (content_type == DW_LNCT_path) != (namepass == 1);
 	      data = read_and_display_attr_value (0, form, 0, start, data, end,
-						  0, 0, linfo->li_offset_size,
+						  0, linfo->li_address_size,
+						  linfo->li_offset_size,
 						  linfo->li_version, NULL,
-			    ((content_type == DW_LNCT_path) != (namepass == 1)),
-						  section, NULL, '\t', -1,
-						  false, 0, 0, false);
+						  do_loc, section, NULL, '\t',
+						  -1, false, 0, 0, false);
 	    }
 	}
 
@@ -6517,8 +6532,9 @@ display_debug_lines_decoded (struct dwarf_section *  section,
 			    }
 			  break;
 			}
-		      data = read_and_display_attr_value (0, form, 0, start,
-							  data, end, 0, 0,
+		      data = read_and_display_attr_value (0, form, 0,
+							  start, data, end, 0,
+							  linfo.li_address_size,
 							  linfo.li_offset_size,
 							  linfo.li_version,
 							  NULL, 1, section,
@@ -6616,8 +6632,9 @@ display_debug_lines_decoded (struct dwarf_section *  section,
 			    }
 			  break;
 			}
-		      data = read_and_display_attr_value (0, form, 0, start,
-							  data, end, 0, 0,
+		      data = read_and_display_attr_value (0, form, 0,
+							  start, data, end, 0,
+							  linfo.li_address_size,
 							  linfo.li_offset_size,
 							  linfo.li_version,
 							  NULL, 1, section,
@@ -7837,7 +7854,8 @@ display_debug_macro (struct dwarf_section *section,
 		      SAFE_BYTE_GET_AND_INC (val, desc, 1, end);
 		      curr
 			= read_and_display_attr_value (0, val, 0,
-						       start, curr, end, 0, 0,
+						       start, curr, end, 0,
+						       offset_size,
 						       offset_size, version,
 						       NULL, 0, section,
 						       NULL, ' ', -1,
@@ -8542,13 +8560,8 @@ display_loclists_unit_header (struct dwarf_section *  section,
   if (length > section->size - header_offset)
     length = section->size - header_offset;
 
-  if (segment_selector_size != 0)
-    {
-      warn (_("The %s section contains an "
-	      "unsupported segment selector size: %d.\n"),
-	    section->name, segment_selector_size);
-      return (uint64_t) -1;
-    }
+  if (!address_size_ok (section->name, address_size, segment_selector_size))
+    return (uint64_t) -1;
 
   uint64_t max_off_count = length >> (is_64bit ? 3 : 2);
   if (*offset_count > max_off_count)
@@ -8628,15 +8641,9 @@ display_debug_loc (struct dwarf_section *section, void *file)
 	}
 
       SAFE_BYTE_GET_AND_INC (address_size, hdrptr, 1, end);
-
       SAFE_BYTE_GET_AND_INC (segment_selector_size, hdrptr, 1, end);
-      if (segment_selector_size != 0)
-	{
-	  warn (_("The %s section contains "
-		  "unsupported segment selector size: %d.\n"),
-		section->name, segment_selector_size);
-	  return 0;
-	}
+      if (!address_size_ok (section->name, address_size, segment_selector_size))
+	return 0;
 
       SAFE_BYTE_GET_AND_INC (offset_entry_count, hdrptr, 4, end);
 
@@ -9014,7 +9021,7 @@ display_debug_aranges (struct dwarf_section *section,
       uint64_t length;
       uint64_t address;
       uint64_t sec_off;
-      unsigned char address_size;
+      unsigned char tuple_size;
       unsigned int offset_size;
       unsigned char *end_ranges;
 
@@ -9071,45 +9078,31 @@ display_debug_aranges (struct dwarf_section *section,
       printf (_("  Pointer Size:             %d\n"), arange.ar_pointer_size);
       printf (_("  Segment Size:             %d\n"), arange.ar_segment_size);
 
-      address_size = arange.ar_pointer_size + arange.ar_segment_size;
-
-      /* PR 17512: file: 001-108546-0.001:0.1.  */
-      if (address_size == 0 || address_size > 8)
-	{
-	  error (_("Invalid address size in %s section!\n"),
-		 section->name);
-	  break;
-	}
+      if (!address_size_ok (section->name, arange.ar_pointer_size,
+			    arange.ar_segment_size))
+	break;
 
-      /* The DWARF spec does not require that the address size be a power
-	 of two, but we do.  This will have to change if we ever encounter
-	 an uneven architecture.  */
-      if ((address_size & (address_size - 1)) != 0)
-	{
-	  warn (_("Pointer size + Segment size is not a power of two.\n"));
-	  break;
-	}
+      tuple_size = 2 * arange.ar_pointer_size + arange.ar_segment_size;
 
-      if (address_size > 4)
+      if (tuple_size > 8)
 	printf (_("\n    Address            Length\n"));
       else
 	printf (_("\n    Address    Length\n"));
 
       addr_ranges = hdrptr;
 
-      /* Must pad to an alignment boundary that is twice the address size.  */
-      addr_ranges += (2 * address_size - 1
-		      - (hdrptr - start - 1) % (2 * address_size));
+      /* Pad to a multiple of the tuple size.  */
+      addr_ranges += tuple_size - 1 - (addr_ranges - start - 1) % tuple_size;
 
-      while (2 * address_size <= end_ranges - addr_ranges)
+      while (tuple_size <= end_ranges - addr_ranges)
 	{
-	  SAFE_BYTE_GET_AND_INC (address, addr_ranges, address_size,
+	  SAFE_BYTE_GET_AND_INC (address, addr_ranges, arange.ar_pointer_size,
 				 end_ranges);
-	  SAFE_BYTE_GET_AND_INC (length, addr_ranges, address_size,
+	  SAFE_BYTE_GET_AND_INC (length, addr_ranges, arange.ar_pointer_size,
 				 end_ranges);
 	  printf ("    ");
-	  print_hex (address, address_size);
-	  print_hex_ns (length, address_size);
+	  print_hex (address, arange.ar_pointer_size);
+	  print_hex_ns (length, arange.ar_pointer_size);
 	  putchar ('\n');
 	}
 
@@ -9187,6 +9180,7 @@ display_debug_addr (struct dwarf_section *section,
     {
       unsigned int idx;
       unsigned int address_size = debug_addr_info [i]->pointer_size;
+      unsigned int segment_selector_size = 0;
 
       printf (_("  For compilation unit at offset %#" PRIx64 ":\n"),
 	      debug_addr_info [i]->cu_offset);
@@ -9199,7 +9193,6 @@ display_debug_addr (struct dwarf_section *section,
 	  unsigned char *curr_header = header;
 	  uint64_t length;
 	  int version;
-	  int segment_selector_size;
 
 	  if (header_size != 8 && header_size != 16)
 	    {
@@ -9226,7 +9219,6 @@ display_debug_addr (struct dwarf_section *section,
 
 	  SAFE_BYTE_GET_AND_INC (address_size, curr_header, 1, entry);
 	  SAFE_BYTE_GET_AND_INC (segment_selector_size, curr_header, 1, entry);
-	  address_size += segment_selector_size;
 	}
       else
 	end = section->start + debug_addr_info [i + 1]->addr_base;
@@ -9234,12 +9226,8 @@ display_debug_addr (struct dwarf_section *section,
       header = end;
       idx = 0;
 
-      if (address_size < 1 || address_size > sizeof (uint64_t))
-	{
-	  warn (_("Corrupt %s section: address size (%x) is wrong\n"),
-		section->name, address_size);
-	  break;
-	}
+      if (!address_size_ok (section->name, address_size, segment_selector_size))
+	break;
 
       while ((size_t) (end - entry) >= address_size)
 	{
@@ -12233,8 +12221,10 @@ display_debug_names (struct dwarf_section *section, void *file)
 		  if (tagno >= 0)
 		    printf (" %s", get_IDX_name (xindex));
 		  entryptr = read_and_display_attr_value (0, form, 0,
-							  unit_start, entryptr, unit_end,
-							  0, 0, offset_size,
+							  unit_start, entryptr,
+							  unit_end, 0,
+							  offset_size,
+							  offset_size,
 							  dwarf_version, NULL,
 							  (tagno < 0), section,
 							  NULL, '=', -1,


More information about the Binutils-cvs mailing list