[llvm-readobj/elf] - Fix a crash when dumping a dynamic relocation that refer to...
authorGeorgii Rymar <grimar@accesssoftek.com>
Tue, 27 Oct 2020 09:15:09 +0000 (12:15 +0300)
committerGeorgii Rymar <grimar@accesssoftek.com>
Thu, 29 Oct 2020 12:38:47 +0000 (15:38 +0300)
There is a possible scenario when we crash when dumping dynamic relocations.
For that we should have no section headers (to take the number of synamic symbols from)
and a dynamic relocation that refers to a symbol with an index that is too large to be in a file.

The patch fixes it.

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

llvm/test/tools/llvm-readobj/ELF/broken-dynamic-reloc.test
llvm/tools/llvm-readobj/ELFDumper.cpp

index e4ca4d7..8ac862d 100644 (file)
@@ -319,3 +319,97 @@ ProgramHeaders:
       - Section: .rela.dyn
       - Section: .rel.dyn
       - Section: .dynamic
+
+## Check that llvm-readobj/llvm-readelf reports a warning when dumping a relocation
+## which refers to a symbol past the end of the file.
+
+# RUN: yaml2obj --docnum=7 %s -o %t7
+# RUN: llvm-readobj --dyn-relocations %t7 2>&1 | \
+# RUN:   FileCheck %s -DFILE=%t7 --check-prefix=PAST-EOF-LLVM
+# RUN: llvm-readelf --dyn-relocations %t7 2>&1 | \
+# RUN:   FileCheck %s -DFILE=%t7 --check-prefix=PAST-EOF-GNU
+
+# PAST-EOF-LLVM:      Dynamic Relocations {
+# PAST-EOF-LLVM-NEXT: warning: '[[FILE]]': unable to get name of the dynamic symbol with index 1: st_name (0x1) is past the end of the string table of size 0x0
+# PAST-EOF-LLVM-NEXT:   0x0 R_X86_64_NONE <corrupt> 0x0
+# PAST-EOF-LLVM-NEXT: warning: '[[FILE]]': unable to get name of the dynamic symbol with index 2: symbol at 0x330 goes past the end of the file (0x330)
+# PAST-EOF-LLVM-NEXT:   0x0 R_X86_64_NONE <corrupt> 0x0
+# PAST-EOF-LLVM-NEXT: warning: '[[FILE]]': unable to get name of the dynamic symbol with index 4294967295: symbol at 0x18000002e8 goes past the end of the file (0x330)
+# PAST-EOF-LLVM-NEXT:   0x0 R_X86_64_NONE <corrupt> 0x0
+# PAST-EOF-LLVM-NEXT: }
+
+# PAST-EOF-GNU:      'RELA' relocation section at offset 0x200 contains 72 bytes:
+# PAST-EOF-GNU-NEXT:     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
+# PAST-EOF-GNU-NEXT: warning: '[[FILE]]': unable to get name of the dynamic symbol with index 1: st_name (0x1) is past the end of the string table of size 0x0
+# PAST-EOF-GNU-NEXT: 0000000000000000  0000000100000000 R_X86_64_NONE          0000000000000000 <corrupt> + 0
+# PAST-EOF-GNU-NEXT: warning: '[[FILE]]': unable to get name of the dynamic symbol with index 2: symbol at 0x330 goes past the end of the file (0x330)
+# PAST-EOF-GNU-NEXT: 0000000000000000  0000000200000000 R_X86_64_NONE                           <corrupt> + 0
+# PAST-EOF-GNU-NEXT: warning: '[[FILE]]': unable to get name of the dynamic symbol with index 4294967295: symbol at 0x18000002e8 goes past the end of the file (0x330)
+# PAST-EOF-GNU-NEXT: 0000000000000000  ffffffff00000000 R_X86_64_NONE                           <corrupt> + 0
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_X86_64
+Sections:
+  - Name:    .dynamic
+    Type:    SHT_DYNAMIC
+    Flags:   [ SHF_ALLOC ]
+    Address: 0x100
+    Offset:  0x100
+    Entries:
+      - Tag:   DT_RELA
+        Value: 0x200
+      - Tag:   DT_SYMTAB
+        Value: 0x300
+      - Tag:   DT_RELASZ
+        Value: 0x48
+      - Tag:   DT_RELAENT
+        Value: 0x18
+      - Tag:   DT_NULL
+        Value: 0x0
+  - Name:    .rela.dyn
+    Type:    SHT_RELA
+    Flags:   [ SHF_ALLOC ]
+    Address: 0x200
+    Offset:  0x200
+    Relocations:
+## This symbol is located right before the EOF, so we can dump it.
+      - Symbol: 0x1
+        Type:   R_X86_64_NONE
+## The next symbol, which goes past the EOF.
+      - Symbol: 0x2
+        Type:   R_X86_64_NONE
+## One more symbol that goes past the EOF
+## with the maximal possible index.
+      - Symbol: 0xFFFFFFFF
+        Type:   R_X86_64_NONE
+## Place all implicit sections here to make the .dynsym section to be the
+## last in the file. This makes the task of validating offsets a bit easier.
+  - Name: .dynstr
+    Type: SHT_STRTAB
+  - Name: .strtab
+    Type: SHT_STRTAB
+  - Name: .shstrtab
+    Type: SHT_STRTAB
+  - Name:    .dynsym
+    Type:    SHT_DYNSYM
+    Flags:   [ SHF_ALLOC ]
+    Address: 0x300
+    Offset:  0x300
+DynamicSymbols:
+  - Name: foo
+ProgramHeaders:
+  - Type:   PT_LOAD
+    Offset: 0x0
+    Sections:
+      - Section: .dynamic
+      - Section: .rela.dyn
+      - Section: .dynsym
+  - Type: PT_DYNAMIC
+    Sections:
+      - Section: .dynamic
+SectionHeaderTable:
+  NoHeaders: true
index 7a796ef..4b8e987 100644 (file)
@@ -4493,7 +4493,8 @@ template <class ELFT>
 RelSymbol<ELFT> getSymbolForReloc(const ELFFile<ELFT> &Obj, StringRef FileName,
                                   const ELFDumper<ELFT> &Dumper,
                                   const Relocation<ELFT> &Reloc) {
-  auto WarnAndReturn = [&](const typename ELFT::Sym *Sym,
+  using Elf_Sym = typename ELFT::Sym;
+  auto WarnAndReturn = [&](const Elf_Sym *Sym,
                            const Twine &Reason) -> RelSymbol<ELFT> {
     reportWarning(
         createError("unable to get name of the dynamic symbol with index " +
@@ -4502,8 +4503,8 @@ RelSymbol<ELFT> getSymbolForReloc(const ELFFile<ELFT> &Obj, StringRef FileName,
     return {Sym, "<corrupt>"};
   };
 
-  ArrayRef<typename ELFT::Sym> Symbols = Dumper.dynamic_symbols();
-  const typename ELFT::Sym *FirstSym = Symbols.begin();
+  ArrayRef<Elf_Sym> Symbols = Dumper.dynamic_symbols();
+  const Elf_Sym *FirstSym = Symbols.begin();
   if (!FirstSym)
     return WarnAndReturn(nullptr, "no dynamic symbol table found");
 
@@ -4516,7 +4517,15 @@ RelSymbol<ELFT> getSymbolForReloc(const ELFFile<ELFT> &Obj, StringRef FileName,
         "index is greater than or equal to the number of dynamic symbols (" +
             Twine(Symbols.size()) + ")");
 
-  const typename ELFT::Sym *Sym = FirstSym + Reloc.Symbol;
+  const uint64_t FileSize = Obj.getBufSize();
+  const uint64_t SymOffset = ((const uint8_t *)FirstSym - Obj.base()) +
+                             (uint64_t)Reloc.Symbol * sizeof(Elf_Sym);
+  if (SymOffset + sizeof(Elf_Sym) > FileSize)
+    return WarnAndReturn(nullptr, "symbol at 0x" + Twine::utohexstr(SymOffset) +
+                                      " goes past the end of the file (0x" +
+                                      Twine::utohexstr(FileSize) + ")");
+
+  const Elf_Sym *Sym = FirstSym + Reloc.Symbol;
   Expected<StringRef> ErrOrName = Sym->getName(Dumper.getDynamicStringTable());
   if (!ErrOrName)
     return WarnAndReturn(Sym, toString(ErrOrName.takeError()));