PR28069, assertion fail in dwarf.c:display_discr_list
authorAlan Modra <amodra@gmail.com>
Sat, 10 Jul 2021 00:04:30 +0000 (09:34 +0930)
committerAlan Modra <amodra@gmail.com>
Sat, 10 Jul 2021 03:53:54 +0000 (13:23 +0930)
We shouldn't be asserting on anything to do with leb128 values, or
reporting file and line numbers when something unexpected happens.
leb128 data is of indeterminate length, perfect for fuzzer mayhem.
It would only make sense to assert or report dwarf.c/readelf.c source
lines if the code had already sized and sanity checked the leb128
values.

After removing the assertions, the testcase then gave:

    <37>   DW_AT_discr_list  : 5 byte block: 0 0 0 0 0  (label 0, label 0, label 0, label 0, <corrupt>
readelf: Warning: corrupt discr_list - unrecognized discriminant byte 0x5

    <3d>   DW_AT_encoding    : 0 (void)
    <3e>   DW_AT_identifier_case: 0 (case_sensitive)
    <3f>   DW_AT_virtuality  : 0 (none)
    <40>   DW_AT_decimal_sign: 5 (trailing separate)

So the DW_AT_discr_list was showing more data than just the 5 byte
block.  That happened due to "end" pointing a long way past the end of
block, and uvalue decrementing past zero on one of the leb128 bytes.

PR 28069
* dwarf.c (display_discr_list): Remove assertions.  Delete "end"
parameter, use initial "data" pointer as the end.  Formatting.
Don't count down bytes as they are read.
(read_and_display_attr_value): Adjust display_discr_list call.
(read_and_print_leb128): Don't pass __FILE__ and __LINE__ to
report_leb_status.
* dwarf.h (report_leb_status): Don't report file and line
numbers.  Delete file and lnum parameters,
(READ_ULEB, READ_SLEB): Adjust.

binutils/ChangeLog
binutils/dwarf.c
binutils/dwarf.h

index be5fd77b8384be11940186fab592a6209ca591b5..6f4946b36aa9ef3fd6d1bdc111c4611d68aae722 100644 (file)
@@ -1,3 +1,16 @@
+2021-07-10  Alan Modra  <amodra@gmail.com>
+
+       PR 28069
+       * dwarf.c (display_discr_list): Remove assertions.  Delete "end"
+       parameter, use initial "data" pointer as the end.  Formatting.
+       Don't count down bytes as they are read.
+       (read_and_display_attr_value): Adjust display_discr_list call.
+       (read_and_print_leb128): Don't pass __FILE__ and __LINE__ to
+       report_leb_status.
+       * dwarf.h (report_leb_status): Don't report file and line
+       numbers.  Delete file and lnum parameters,
+       (READ_ULEB, READ_SLEB): Adjust.
+
 2021-07-07  Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>
 
        * configure.ac: Check for strnlen declaration.
index ad1ee97d9bf54781a0cdc6fc18790a416b263110..fbd5d6aea5a4019ecf48e17f1fb55563cd6ef370 100644 (file)
@@ -2280,7 +2280,7 @@ read_and_print_leb128 (unsigned char *data,
   int status;
   dwarf_vma val = read_leb128 (data, end, is_signed, bytes_read, &status);
   if (status != 0)
-    report_leb_status (status, __FILE__, __LINE__);
+    report_leb_status (status);
   else
     printf ("%s", dwarf_vmatoa (is_signed ? "d" : "u", val));
 }
@@ -2289,9 +2289,10 @@ static void
 display_discr_list (unsigned long          form,
                    dwarf_vma              uvalue,
                    unsigned char *        data,
-                   unsigned const char *  end,
                    int                    level)
 {
+  unsigned char *end = data;
+
   if (uvalue == 0)
     {
       printf ("[default]");
@@ -2320,41 +2321,32 @@ display_discr_list (unsigned long          form,
       return;
     }
 
-  bool is_signed =
-    (level > 0 && level <= MAX_CU_NESTING)
-    ? level_type_signed [level - 1] : false;
+  bool is_signed = (level > 0 && level <= MAX_CU_NESTING
+                   ? level_type_signed [level - 1] : false);
 
   printf ("(");
-  while (uvalue)
+  while (data < end)
     {
       unsigned char     discriminant;
       unsigned int      bytes_read;
 
       SAFE_BYTE_GET_AND_INC (discriminant, data, 1, end);
-      -- uvalue;
 
-      assert (uvalue > 0);
       switch (discriminant)
        {
        case DW_DSC_label:
          printf ("label ");
          read_and_print_leb128 (data, & bytes_read, end, is_signed);
-         assert (bytes_read <= uvalue && bytes_read > 0);
-         uvalue -= bytes_read;
          data += bytes_read;
          break;
 
        case DW_DSC_range:
          printf ("range ");
          read_and_print_leb128 (data, & bytes_read, end, is_signed);
-         assert (bytes_read <= uvalue && bytes_read > 0);
-         uvalue -= bytes_read;
          data += bytes_read;
 
          printf ("..");
          read_and_print_leb128 (data, & bytes_read, end, is_signed);
-         assert (bytes_read <= uvalue && bytes_read > 0);
-         uvalue -= bytes_read;
          data += bytes_read;
          break;
 
@@ -2365,7 +2357,7 @@ display_discr_list (unsigned long          form,
          return;
        }
 
-      if (uvalue)
+      if (data < end)
        printf (", ");
     }
 
@@ -3230,7 +3222,7 @@ read_and_display_attr_value (unsigned long           attribute,
 
     case DW_AT_discr_list:
       printf ("\t");
-      display_discr_list (form, uvalue, data, end, level);
+      display_discr_list (form, uvalue, data, level);
       break;
 
     case DW_AT_frame_base:
index 2070c6f3daf8f86eb9b4176fb697932d2f7b0279..887b720f632b4220c36c5b40ea5ff0bffbe617dc 100644 (file)
@@ -264,12 +264,12 @@ extern unsigned char * get_build_id (void *);
 #endif
 
 static inline void
-report_leb_status (int status, const char *file, unsigned long lnum)
+report_leb_status (int status)
 {
   if ((status & 1) != 0)
-    error (_("%s:%lu: end of data encountered whilst reading LEB\n"), file, lnum);
+    error (_("end of data encountered whilst reading LEB\n"));
   else if ((status & 2) != 0)
-    error (_("%s:%lu: read LEB value is too large to store in destination variable\n"), file, lnum);
+    error (_("read LEB value is too large to store in destination variable\n"));
 }
 
 #define SKIP_ULEB(start, end)                                  \
@@ -302,7 +302,7 @@ report_leb_status (int status, const char *file, unsigned long lnum)
       (var) = _val;                                            \
       if ((var) != _val)                                       \
        _status |= 2;                                           \
-      report_leb_status (_status, __FILE__, __LINE__);         \
+      report_leb_status (_status);                             \
     }                                                          \
   while (0)
 
@@ -318,6 +318,6 @@ report_leb_status (int status, const char *file, unsigned long lnum)
       (var) = _val;                                            \
       if ((var) != _val)                                       \
        _status |= 2;                                           \
-      report_leb_status (_status, __FILE__, __LINE__);         \
+      report_leb_status (_status);                             \
     }                                                          \
   while (0)