Also found by afl-fuzz on the varlocs testcase.
DW_FORM_data16 is constant from 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 <m...@klomp.org>
---
 libdw/dwarf_getlocation.c | 44 +++++++++++++++++++++++++++++++-------------
 src/ChangeLog             |  5 +++++
 src/readelf.c             | 15 ++++++++++-----
 3 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c
index 7f294fe..fc59a2a 100644
--- a/libdw/dwarf_getlocation.c
+++ b/libdw/dwarf_getlocation.c
@@ -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);
diff --git a/src/ChangeLog b/src/ChangeLog
index 8fb418a..9b119b6 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,8 @@
+2018-06-15  Mark Wielaard  <m...@klomp.org>
+
+       * readelf.c (attr_callback): Only print block as expressions if it
+       isn't DW_FORM_data16.
+
 2018-06-16  Mark Wielaard  <m...@klomp.org>
 
        * readelf.c (print_debug_loc_section): Make sure next_off doesn't
diff --git a/src/readelf.c b/src/readelf.c
index 2e7378e..4172046 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -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;
-- 
1.8.3.1

Reply via email to