From 596245135106b2a965d809e272dc7c758afdc98f Mon Sep 17 00:00:00 2001
From: Nick Clifton <nickc@redhat.com>
Date: Wed, 21 Oct 2020 17:42:42 +0100
Subject: [PATCH] Work around problem in DWARF decoding library which can
 result in attempts to read arbitrary bytes as if they were an LEB128 encoded
 value.

	* dwarf.c (skip_attr_bytes): Accept DWARF versions higher than 4
	when processing the DW_FORM_ref_addr form.
	Skip bytes in DW_FORM_block and DW_FORM_exprloc forms.
	Handle DW_FORM_indirect.
	(get_type_signedness): Allow a limited amount of recursion.
	Do not attempt to decode types that use the DW_FORM_ref_addr form.
	(read_and_display_attr_value): 	Do not attempt to decode types
	that use the DW_FORM_ref_addr form.
---
 binutils/dwarf.c   | 43 +++++++++++++++++++++++++------------------
 1 files changed, 25 insertions(+), 18 deletions(-)

diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index e152b20438cc..c842aba6c1e7 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -1868,7 +1868,7 @@ skip_attr_bytes (unsigned long          form,
     case DW_FORM_ref_addr:
       if (dwarf_version == 2)
 	SAFE_BYTE_GET_AND_INC (uvalue, data, pointer_size, end);
-      else if (dwarf_version == 3 || dwarf_version == 4)
+      else if (dwarf_version > 2)
 	SAFE_BYTE_GET_AND_INC (uvalue, data, offset_size, end);
       else
 	return NULL;
@@ -1920,6 +1920,7 @@ skip_attr_bytes (unsigned long          form,
 
     case DW_FORM_ref8:
     case DW_FORM_data8:
+    case DW_FORM_ref_sig8:
       data += 8;
       break;
 
@@ -1934,6 +1935,7 @@ skip_attr_bytes (unsigned long          form,
     case DW_FORM_block:
     case DW_FORM_exprloc:
       READ_ULEB (uvalue, data, end);
+      data += uvalue;
       break;
 
     case DW_FORM_block1:
@@ -1951,12 +1953,12 @@ skip_attr_bytes (unsigned long          form,
       data += 4 + uvalue;
       break;
 
-    case DW_FORM_ref_sig8:
-      data += 8;
-      break;
-
     case DW_FORM_indirect:
-      /* FIXME: Handle this form.  */
+      READ_ULEB (form, data, end);
+      if (form == DW_FORM_implicit_const)
+	SKIP_ULEB (data, end);
+      return skip_attr_bytes (form, data, end, pointer_size, offset_size, dwarf_version, value_return);
+
     default:
       return NULL;
     }
@@ -1978,7 +1980,7 @@ get_type_signedness (unsigned char *        start,
 		     dwarf_vma              offset_size,
 		     int                    dwarf_version,
 		     bfd_boolean *          is_signed,
-		     bfd_boolean	    is_nested)
+		     unsigned int	    nesting)
 {
   unsigned long   abbrev_number;
   abbrev_entry *  entry;
@@ -1997,6 +1999,14 @@ get_type_signedness (unsigned char *        start,
     /* FIXME: Issue a warning ?  */
     return;
 
+#define MAX_NESTING 20
+  if (nesting > MAX_NESTING)
+    {
+      /* FIXME: Warn - or is this expected ?
+	 NB/ We need to avoid infinite recursion.  */
+      return;
+    }
+
   for (attr = entry->first_attr;
        attr != NULL && attr->attribute;
        attr = attr->next)
@@ -2019,16 +2029,12 @@ get_type_signedness (unsigned char *        start,
 #endif
 	case DW_AT_type:
 	  /* Recurse.  */
-	  if (is_nested)
-	    {
-	      /* FIXME: Warn - or is this expected ?
-		 NB/ We need to avoid infinite recursion.  */
-	      return;
-	    }
 	  if (uvalue >= (size_t) (end - start))
 	    return;
-	  get_type_signedness (start, start + uvalue, end, pointer_size,
-			       offset_size, dwarf_version, is_signed, TRUE);
+	  /* We cannot correctly process DW_FORM_ref_addr at the moment.  */
+	  if (attr->form != DW_FORM_ref_addr)
+	    get_type_signedness (start, start + uvalue, end, pointer_size,
+				 offset_size, dwarf_version, is_signed, nesting + 1);
 	  break;
 
 	case DW_AT_encoding:
@@ -2206,7 +2212,6 @@ read_and_display_attr_value (unsigned long           attribute,
 	SAFE_BYTE_GET_AND_INC (uvalue, data, offset_size, end);
       else
 	error (_("Internal error: DWARF version is not 2, 3 or 4.\n"));
-
       break;
 
     case DW_FORM_addr:
@@ -2663,8 +2668,10 @@ read_and_display_attr_value (unsigned long           attribute,
 	{
 	  bfd_boolean is_signed = FALSE;
 
-	  get_type_signedness (start, start + uvalue, end, pointer_size,
-			       offset_size, dwarf_version, & is_signed, FALSE);
+	  /* We cannot correctly process DW_FORM_ref_addr at the moment.  */
+	  if (form != DW_FORM_ref_addr)
+	    get_type_signedness (start, start + uvalue, end, pointer_size,
+				 offset_size, dwarf_version, & is_signed, 0);
 	  level_type_signed[level] = is_signed;
 	}
       break;
