PR22216, infinite loop in readelf process_symbol_table
authorAlan Modra <amodra@gmail.com>
Wed, 27 Sep 2017 05:44:00 +0000 (15:14 +0930)
committerAlan Modra <amodra@gmail.com>
Wed, 27 Sep 2017 06:24:18 +0000 (15:54 +0930)
This should make readelf bombproof given a fuzzed DT_HASH.  Also
removes a bogus check that would have resulted in wrong histograms.

PR 22216
* readelf.c (process_symbol_table): Check that DT_HASH symbol
chains are only visited once, and report an error if not.  Display
invalid symbol index if chain is out of range.  Use the same logic
when calculating histograms rather than the PR 17531 fix.  Delete
bogus check that chained index is less than number of buckets.

binutils/ChangeLog
binutils/readelf.c

index dc4faf553f0830e1bdf3680b8156375848857dc3..a4de14c3a39d3375ab5a46cc2db9a1d915361ed1 100644 (file)
@@ -1,3 +1,12 @@
+2017-09-27  Alan Modra  <amodra@gmail.com>
+
+       PR 22216
+       * readelf.c (process_symbol_table): Check that DT_HASH symbol
+       chains are only visited once, and report an error if not.  Display
+       invalid symbol index if chain is out of range.  Use the same logic
+       when calculating histograms rather than the PR 17531 fix.  Delete
+       bogus check that chained index is less than number of buckets.
+
 2017-09-26  Nick Clifton  <nickc@redhat.com>
 
        PR 22154
index 3e5ad0e4c5c9e818d11be57fa1a2f51d90d98e6c..260aedf65b5d1b25a949db66421f9743700ce978 100644 (file)
@@ -11436,6 +11436,7 @@ process_symbol_table (FILE * file)
       if (dynamic_info[DT_HASH])
        {
          bfd_vma si;
+         char *visited;
 
          printf (_("\nSymbol table for image:\n"));
          if (is_32bit_elf)
@@ -11443,14 +11444,22 @@ process_symbol_table (FILE * file)
          else
            printf (_("  Num Buc:    Value          Size   Type   Bind Vis      Ndx Name\n"));
 
+         visited = xcmalloc (nchains, 1);
+         memset (visited, 0, nchains);
          for (hn = 0; hn < nbuckets; hn++)
            {
-             if (! buckets[hn])
-               continue;
-
-             for (si = buckets[hn]; si < nchains && si > 0; si = chains[si])
-               print_dynamic_symbol (si, hn);
+             for (si = buckets[hn]; si > 0; si = chains[si])
+               {
+                 print_dynamic_symbol (si, hn);
+                 if (si >= nchains || visited[si])
+                   {
+                     error (_("histogram chain is corrupt\n"));
+                     break;
+                   }
+                 visited[si] = 1;
+               }
            }
+         free (visited);
        }
 
       if (dynamic_info_DT_GNU_HASH)
@@ -11609,7 +11618,7 @@ process_symbol_table (FILE * file)
       unsigned long maxlength = 0;
       unsigned long nzero_counts = 0;
       unsigned long nsyms = 0;
-      unsigned long chained;
+      char *visited;
 
       printf (_("\nHistogram for bucket list length (total of %lu buckets):\n"),
              (unsigned long) nbuckets);
@@ -11620,28 +11629,26 @@ process_symbol_table (FILE * file)
          error (_("Out of memory allocating space for histogram buckets\n"));
          return FALSE;
        }
+      visited = xcmalloc (nchains, 1);
+      memset (visited, 0, nchains);
 
       printf (_(" Length  Number     %% of total  Coverage\n"));
       for (hn = 0; hn < nbuckets; ++hn)
        {
-         for (si = buckets[hn], chained = 0;
-              si > 0 && si < nchains && si < nbuckets && chained <= nchains;
-              si = chains[si], ++chained)
+         for (si = buckets[hn]; si > 0; si = chains[si])
            {
              ++nsyms;
              if (maxlength < ++lengths[hn])
                ++maxlength;
+             if (si >= nchains || visited[si])
+               {
+                 error (_("histogram chain is corrupt\n"));
+                 break;
+               }
+             visited[si] = 1;
            }
-
-           /* PR binutils/17531: A corrupt binary could contain broken
-              histogram data.  Do not go into an infinite loop trying
-              to process it.  */
-           if (chained > nchains)
-             {
-               error (_("histogram chain is corrupt\n"));
-               break;
-             }
        }
+      free (visited);
 
       counts = (unsigned long *) calloc (maxlength + 1, sizeof (*counts));
       if (counts == NULL)