libdw, readelf: Don't handle DW_FORM_data16 as expression block/location.
authorMark Wielaard <mark@klomp.org>
Fri, 15 Jun 2018 14:25:15 +0000 (16:25 +0200)
committerMark Wielaard <mark@klomp.org>
Sun, 17 Jun 2018 18:32:06 +0000 (20:32 +0200)
Also found by afl-fuzz on the varlocs testcase.
DW_FORM_data16 is constant form according to the DWARF5 spec.
But since it is 128bits it isn't really representable as Dwarf_Word.
So we treat it as block form. But we cannot treat it as an expression
block. Make sure readelf prints it as a regular block and that
dwarf_getlocation[s|_addr] doesn't treat it as location expression.

Signed-off-by: Mark Wielaard <mark@klomp.org>
libdw/ChangeLog
libdw/dwarf_getlocation.c
src/ChangeLog
src/readelf.c

index 6492c97..329a994 100644 (file)
@@ -1,3 +1,11 @@
+2018-06-15  Mark Wielaard  <mark@klomp.org>
+
+       * dwarf_getlocation.c (check_constant_offset): Clarify DW_FORM_data16
+       isn't really a constant.
+       (dwarf_getlocation): Don't handle DW_FORM_data16 as block.
+       (dwarf_getlocation_addr): Likewise.
+       (dwarf_getlocations): Likewise.
+
 2018-06-12  Mark Wielaard  <mark@klomp.org>
 
        * memory-access.h (read_3ubyte_unaligned_inc): New define.
index 7f294fe..fc59a2a 100644 (file)
@@ -174,6 +174,8 @@ check_constant_offset (Dwarf_Attribute *attr,
     default:
       return 1;
 
+      /* Note, we don't regard DW_FORM_data16 as a constant form,
+        even though technically it is according to the standard.  */
     case DW_FORM_data1:
     case DW_FORM_data2:
     case DW_FORM_data4:
@@ -665,7 +667,13 @@ dwarf_getlocation (Dwarf_Attribute *attr, Dwarf_Op **llbuf, size_t *listlen)
   if (result != 1)
     return result;
 
-  /* If it has a block form, it's a single location expression.  */
+  /* If it has a block form, it's a single location expression.
+     Except for DW_FORM_data16, which is a 128bit constant.  */
+  if (attr->form == DW_FORM_data16)
+    {
+      __libdw_seterrno (DWARF_E_NO_BLOCK);
+      return -1;
+    }
   Dwarf_Block block;
   if (INTUSE(dwarf_formblock) (attr, &block) != 0)
     return -1;
@@ -863,9 +871,11 @@ dwarf_getlocation_addr (Dwarf_Attribute *attr, Dwarf_Addr address,
   if (llbufs == NULL)
     maxlocs = SIZE_MAX;
 
-  /* If it has a block form, it's a single location expression.  */
+  /* If it has a block form, it's a single location expression.
+     Except for DW_FORM_data16, which is a 128bit constant.  */
   Dwarf_Block block;
-  if (INTUSE(dwarf_formblock) (attr, &block) == 0)
+  if (attr->form != DW_FORM_data16
+      && INTUSE(dwarf_formblock) (attr, &block) == 0)
     {
       if (maxlocs == 0)
        return 0;
@@ -876,11 +886,14 @@ dwarf_getlocation_addr (Dwarf_Attribute *attr, Dwarf_Addr address,
       return listlens[0] == 0 ? 0 : 1;
     }
 
-  int error = INTUSE(dwarf_errno) ();
-  if (unlikely (error != DWARF_E_NO_BLOCK))
+  if (attr->form != DW_FORM_data16)
     {
-      __libdw_seterrno (error);
-      return -1;
+      int error = INTUSE(dwarf_errno) ();
+      if (unlikely (error != DWARF_E_NO_BLOCK))
+       {
+         __libdw_seterrno (error);
+         return -1;
+       }
     }
 
   int result = check_constant_offset (attr, &llbufs[0], &listlens[0]);
@@ -938,9 +951,11 @@ dwarf_getlocations (Dwarf_Attribute *attr, ptrdiff_t offset, Dwarf_Addr *basep,
 
   if (offset == 0)
     {
-      /* If it has a block form, it's a single location expression.  */
+      /* If it has a block form, it's a single location expression.
+        Except for DW_FORM_data16, which is a 128bit constant.  */
       Dwarf_Block block;
-      if (INTUSE(dwarf_formblock) (attr, &block) == 0)
+      if (attr->form != DW_FORM_data16
+         && INTUSE(dwarf_formblock) (attr, &block) == 0)
        {
          if (getlocation (attr->cu, &block, expr, exprlen,
                           cu_sec_idx (attr->cu)) != 0)
@@ -952,11 +967,14 @@ dwarf_getlocations (Dwarf_Attribute *attr, ptrdiff_t offset, Dwarf_Addr *basep,
          return 1;
        }
 
-      int error = INTUSE(dwarf_errno) ();
-      if (unlikely (error != DWARF_E_NO_BLOCK))
+      if (attr->form != DW_FORM_data16)
        {
-         __libdw_seterrno (error);
-         return -1;
+         int error = INTUSE(dwarf_errno) ();
+         if (unlikely (error != DWARF_E_NO_BLOCK))
+           {
+             __libdw_seterrno (error);
+             return -1;
+           }
        }
 
       int result = check_constant_offset (attr, expr, exprlen);
index 805a1bf..54bb925 100644 (file)
@@ -1,3 +1,8 @@
+2018-06-15  Mark Wielaard  <mark@klomp.org>
+
+       * readelf.c (attr_callback): Only print block as expressions if it
+       isn't DW_FORM_data16.
+
 2018-06-12  Mark Wielaard  <mark@klomp.org>
 
        * readelf.c (print_form_data): Check we have 4, not 2, bytes
index 2e7378e..4172046 100644 (file)
@@ -7483,11 +7483,16 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
        case DW_AT_GNU_call_site_data_value:
        case DW_AT_GNU_call_site_target:
        case DW_AT_GNU_call_site_target_clobbered:
-         putchar ('\n');
-         print_ops (cbargs->dwflmod, cbargs->dbg,
-                    12 + level * 2, 12 + level * 2,
-                    cbargs->version, cbargs->addrsize, cbargs->offset_size,
-                    attrp->cu, block.length, block.data);
+         if (form != DW_FORM_data16)
+           {
+             putchar ('\n');
+             print_ops (cbargs->dwflmod, cbargs->dbg,
+                        12 + level * 2, 12 + level * 2,
+                        cbargs->version, cbargs->addrsize, cbargs->offset_size,
+                        attrp->cu, block.length, block.data);
+           }
+         else
+           print_block (block.length, block.data);
          break;
        }
       break;