[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Error out for invalid DW_FORM_ref_addr



Hi,

When running dwz on a file that contains invalid DW_FORM_ref_addr attributes
(which has been observed to be generated by a google go compiler) we run
either into an assert:
...
$ dwz f
dwz: dwz.c:8792: write_die: Assertion `refd != NULL' failed.
Aborted (core dumped)
...
or a segmentation fault:
...
$ dwz -l0 f
Segmentation fault (core dumped)
...

Fix this by verifying the DW_FORM_ref_addr attributes after parsing the
.debug_info section.

This gives a detailed error message in regular mode:
...
$ dwz f
dwz: Couldn't find DIE at DW_FORM_ref_addr offset 0x85e89c (referenced by \
     DW_AT_abstract_origin in DIE at offset 0x85eacb)
...
and a more basic error message in low-memory mode:
...
$ dwz -l0 f
dwz: Couldn't find DIE at DW_FORM_ref_addr offset 0x85e848
...

OK for trunk?

Thanks,
- Tom

Error out for invalid DW_FORM_ref_addr

2019-02-05  Tom de Vries  <tdevries@suse.de>

	PR dwz/24169
	* dwz.c (verify_ref_addr_die, verify_ref_addr_low_mem, verify_ref_addr):
	New function.
	(read_debug_info): Call verify_ref_addr.

---
 dwz.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/dwz.c b/dwz.c
index e4e5d5c..7ec1347 100644
--- a/dwz.c
+++ b/dwz.c
@@ -4401,6 +4401,81 @@ collapse_children (dw_cu_ref cu, dw_die_ref die)
       }
 }
 
+/* Verify DW_FORM_ref_addr in DIE.  */
+static inline void
+verify_ref_addr_die (dw_cu_ref cu, dw_die_ref die)
+{
+  dw_die_ref child;
+  for (child = die->die_child; child; child = child->die_sib)
+    verify_ref_addr_die (cu, child);
+
+  if (die->die_offset == -1U)
+    return;
+
+  struct abbrev_tag *t = die->die_abbrev;
+  unsigned int i;
+  unsigned char *section_ptr;
+  unsigned char *ptr;
+  if (unlikely (fi_multifile) && cu->cu_kind == CU_ALT)
+    section_ptr = alt_data[DEBUG_INFO];
+  else if (cu->cu_kind == CU_TYPES)
+    section_ptr = debug_sections[DEBUG_TYPES].data;
+  else
+    section_ptr = debug_sections[DEBUG_INFO].data;
+  ptr = section_ptr + die->die_offset;
+  read_uleb128 (ptr);
+  for (i = 0; i < t->nattr; ++i)
+    {
+      struct abbrev_attr *attr = &t->attr[i];
+      if (attr->form == DW_FORM_ref_addr)
+	{
+	  uint64_t value;
+	  dw_die_ref ref;
+
+	  value = read_size (ptr, cu->cu_version == 2
+			     ? ptr_size : 4);
+
+	  ref = off_htab_lookup (cu, value);
+	  if (ref == NULL)
+	    {
+	      error (1, 0,
+		     "Couldn't find DIE at DW_FORM_ref_addr offset 0x%lx"
+		     " (referenced by %s in DIE at offset 0x%x)",
+		     value, get_DW_AT_str (attr->attr), die->die_offset);
+
+	    }
+	}
+      ptr = skip_attr (cu, attr, ptr);
+    }
+}
+
+/* Verify DW_FORM_ref_addr in low_mem mode.  */
+static int
+verify_ref_addr_low_mem (void **slot, void *data  __attribute__((unused)))
+{
+  dw_die_ref die = (dw_die_ref) *slot;
+  if (die->die_tag == 0)
+    error (1, 0, "Couldn't find DIE at DW_FORM_ref_addr offset 0x%x",
+	   die->die_offset);
+
+  return 1;
+}
+
+/* Verify DW_FORM_ref_addr.  */
+static void
+verify_ref_addr (void)
+{
+  if (unlikely (low_mem))
+    htab_traverse (off_htab, verify_ref_addr_low_mem, NULL);
+  else
+    {
+      dw_cu_ref cu;
+
+      for (cu = first_cu; cu; cu = cu->cu_next)
+	verify_ref_addr_die (cu, cu->cu_die);
+    }
+}
+
 /* First phase of the DWARF compression.  Parse .debug_info section
    (for kind == DEBUG_INFO) or .debug_types section (for kind == DEBUG_TYPES)
    for each CU in it construct internal represetnation for the CU
@@ -4428,6 +4503,7 @@ read_debug_info (DSO *dso, int kind)
   bool low_mem_phase1 = low_mem && kind == DEBUG_INFO;
   struct dw_cu cu_buf;
   struct dw_die die_buf;
+  bool seen_ref_addr = false;
 
   if (likely (!fi_multifile && kind != DEBUG_TYPES))
     {
@@ -4523,6 +4599,9 @@ read_debug_info (DSO *dso, int kind)
 	{
 	  if (ptr == endcu)
 	    {
+	      if (seen_ref_addr)
+		verify_ref_addr ();
+
 	      dw_cu_ref cuf = cu_tail ? cu_tail->cu_next : first_cu;
 	      /* Inside of optimize_multifile, DIE hashes are computed
 		 only after all the CUs from a particular DSO or
@@ -4748,6 +4827,7 @@ read_debug_info (DSO *dso, int kind)
 	      switch (form)
 		{
 		case DW_FORM_ref_addr:
+		  seen_ref_addr = true;
 		  if (unlikely (low_mem_phase1))
 		    {
 		      dw_die_ref ref;
@@ -5019,6 +5099,9 @@ read_debug_info (DSO *dso, int kind)
       goto low_mem_phase2;
     }
 
+  if (seen_ref_addr && !(unlikely (op_multifile)))
+    verify_ref_addr ();
+
   if (unlikely (low_mem))
     ;
   else if (unlikely (meta_abbrev_htab != NULL))