[llvm-readelf] - Do not enter an infinite loop when printing histogram.
authorGeorge Rimar <grimar@accesssoftek.com>
Thu, 10 Oct 2019 13:26:26 +0000 (13:26 +0000)
committerGeorge Rimar <grimar@accesssoftek.com>
Thu, 10 Oct 2019 13:26:26 +0000 (13:26 +0000)
This is similar to D68086.
We are entering an infinite loop when dumping a histogram for a specially crafted
.hash section with a loop in a chain.

Differential revision: https://reviews.llvm.org/D68771

llvm-svn: 374344

llvm/test/tools/llvm-readobj/elf-hash-histogram.test
llvm/tools/llvm-readobj/ELFDumper.cpp

index 13aa513..e0c29d1 100644 (file)
@@ -1,27 +1,70 @@
-RUN: llvm-readelf --elf-hash-histogram %p/Inputs/gnuhash.so.elf-ppc64 \
-RUN:   | FileCheck %s -check-prefix PPC64GNU
-RUN: llvm-readelf --elf-hash-histogram %p/Inputs/gnuhash.so.elf-x86_64 \
-RUN:   | FileCheck %s -check-prefix X86GNU
-RUN: llvm-readelf --elf-hash-histogram %p/Inputs/got-plt.exe.elf-mipsel \
-RUN:   | FileCheck %s -check-prefix SYSV
+RUN: llvm-readelf --elf-hash-histogram %p/Inputs/gnuhash.so.elf-ppc64 \
+RUN:   | FileCheck %s -check-prefix PPC64GNU
+RUN: llvm-readelf --elf-hash-histogram %p/Inputs/gnuhash.so.elf-x86_64 \
+RUN:   | FileCheck %s -check-prefix X86GNU
+RUN: llvm-readelf --elf-hash-histogram %p/Inputs/got-plt.exe.elf-mipsel \
+RUN:   | FileCheck %s -check-prefix SYSV
 
-PPC64GNU: Histogram for `.gnu.hash' bucket list length (total of 3 buckets)
-PPC64GNU-NEXT:  Length  Number     % of total  Coverage
-PPC64GNU-NEXT:       0  1          ( 33.3%)       0.0%
-PPC64GNU-NEXT:       1  1          ( 33.3%)      25.0%
-PPC64GNU-NEXT:       2  0          (  0.0%)      25.0%
-PPC64GNU-NEXT:       3  1          ( 33.3%)     100.0%
+PPC64GNU: Histogram for `.gnu.hash' bucket list length (total of 3 buckets)
+PPC64GNU-NEXT:  Length  Number     % of total  Coverage
+PPC64GNU-NEXT:       0  1          ( 33.3%)       0.0%
+PPC64GNU-NEXT:       1  1          ( 33.3%)      25.0%
+PPC64GNU-NEXT:       2  0          (  0.0%)      25.0%
+PPC64GNU-NEXT:       3  1          ( 33.3%)     100.0%
 
-X86GNU: Histogram for `.gnu.hash' bucket list length (total of 3 buckets)
-X86GNU-NEXT:  Length  Number     % of total  Coverage
-X86GNU-NEXT:       0  1          ( 33.3%)       0.0%
-X86GNU-NEXT:       1  1          ( 33.3%)      25.0%
-X86GNU-NEXT:       2  0          (  0.0%)      25.0%
-X86GNU-NEXT:       3  1          ( 33.3%)     100.0%
+X86GNU: Histogram for `.gnu.hash' bucket list length (total of 3 buckets)
+X86GNU-NEXT:  Length  Number     % of total  Coverage
+X86GNU-NEXT:       0  1          ( 33.3%)       0.0%
+X86GNU-NEXT:       1  1          ( 33.3%)      25.0%
+X86GNU-NEXT:       2  0          (  0.0%)      25.0%
+X86GNU-NEXT:       3  1          ( 33.3%)     100.0%
 
-SYSV: Histogram for bucket list length (total of 3 buckets)
-SYSV-NEXT:  Length  Number     % of total  Coverage
-SYSV-NEXT:       0  0          (  0.0%)       0.0%
-SYSV-NEXT:       1  0          (  0.0%)       0.0%
-SYSV-NEXT:       2  2          ( 66.7%)      57.1%
-SYSV-NEXT:       3  1          ( 33.3%)     100.0%
+# SYSV: Histogram for bucket list length (total of 3 buckets)
+# SYSV-NEXT:  Length  Number     % of total  Coverage
+# SYSV-NEXT:       0  0          (  0.0%)       0.0%
+# SYSV-NEXT:       1  0          (  0.0%)       0.0%
+# SYSV-NEXT:       2  2          ( 66.7%)      57.1%
+# SYSV-NEXT:       3  1          ( 33.3%)     100.0%
+
+## Show that we report a warning for a hash table which contains an entry of
+## the bucket array pointing to a cycle.
+
+# RUN: yaml2obj %s -o %t.o
+# RUN: llvm-readelf --elf-hash-histogram 2>&1 %t.o | FileCheck -DFILE=%t.o %s --check-prefix BROKEN
+
+# BROKEN:       warning: '[[FILE]]': .hash section is invalid: bucket 1: a cycle was detected in the linked chain
+# BROKEN:       Histogram for bucket list length (total of 1 buckets)
+# BROKEN-NEXT:  Length  Number     % of total  Coverage
+# BROKEN-NEXT:       0  0          (  0.0%)       0.0%
+# BROKEN-NEXT:       1  1          (100.0%)     100.0%
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS32
+  Data:    ELFDATA2LSB
+  Type:    ET_REL
+  Machine: EM_386
+Sections:
+  - Name:   .hash
+    Type:   SHT_HASH
+    Link:   .dynsym
+    Bucket: [ 1 ]
+    Chain:  [ 0, 1 ]
+  - Name:  .dynamic
+    Type:  SHT_DYNAMIC
+    Flags: [ SHF_ALLOC ]
+    Entries:
+## llvm-readelf will read the hash table from the file offset
+## p_offset + (p_vaddr - DT_HASH) = p_offset + (0 - 0) = p_offset,
+## which is the start of PT_LOAD, i.e. the file offset of .hash.
+      - Tag:   DT_HASH
+        Value: 0x0
+      - Tag:   DT_NULL
+        Value: 0
+DynamicSymbols:
+  - Name: foo
+ProgramHeaders:
+  - Type:  PT_LOAD
+    Sections:
+      - Section: .hash
+      - Section: .dynamic
index 4e9cf21..1356245 100644 (file)
@@ -3968,9 +3968,21 @@ void GNUStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) {
     // Go over all buckets and and note chain lengths of each bucket (total
     // unique chain lengths).
     for (size_t B = 0; B < NBucket; B++) {
-      for (size_t C = Buckets[B]; C > 0 && C < NChain; C = Chains[C])
+      std::vector<bool> Visited(NChain);
+      for (size_t C = Buckets[B]; C < NChain; C = Chains[C]) {
+        if (C == ELF::STN_UNDEF)
+          break;
+        if (Visited[C]) {
+          reportWarning(
+              createError(".hash section is invalid: bucket " + Twine(C) +
+                          ": a cycle was detected in the linked chain"),
+              this->FileName);
+          break;
+        }
+        Visited[C] = true;
         if (MaxChain <= ++ChainLen[B])
           MaxChain++;
+      }
       TotalSyms += ChainLen[B];
     }