[lld] r291524 - ELF: Reserve space for copy relocations of read-only symbols in relro.

Peter Collingbourne via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 9 17:21:51 PST 2017


Author: pcc
Date: Mon Jan  9 19:21:50 2017
New Revision: 291524

URL: http://llvm.org/viewvc/llvm-project?rev=291524&view=rev
Log:
ELF: Reserve space for copy relocations of read-only symbols in relro.

When reserving copy relocation space for a shared symbol, scan the DSO's
program headers to see if the symbol is in a read-only segment. If so,
reserve space for that symbol in a new synthetic section named .bss.rel.ro
which will be covered by the relro program header.

This fixes the security issue disclosed on the binutils mailing list at:
https://sourceware.org/ml/libc-alpha/2016-12/msg00914.html

Differential Revision: https://reviews.llvm.org/D28272

Added:
    lld/trunk/test/ELF/Inputs/relocation-copy-relro.s
    lld/trunk/test/ELF/relocation-copy-relro.s
Modified:
    lld/trunk/ELF/OutputSections.h
    lld/trunk/ELF/Relocations.cpp
    lld/trunk/ELF/Symbols.cpp
    lld/trunk/ELF/Symbols.h
    lld/trunk/ELF/SyntheticSections.cpp
    lld/trunk/ELF/Writer.cpp
    lld/trunk/test/ELF/Inputs/copy-rel-pie.s

Modified: lld/trunk/ELF/OutputSections.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/OutputSections.h?rev=291524&r1=291523&r2=291524&view=diff
==============================================================================
--- lld/trunk/ELF/OutputSections.h (original)
+++ lld/trunk/ELF/OutputSections.h Mon Jan  9 19:21:50 2017
@@ -206,6 +206,7 @@ template <class ELFT> struct Out {
   static uint8_t First;
   static EhOutputSection<ELFT> *EhFrame;
   static OutputSection<ELFT> *Bss;
+  static OutputSection<ELFT> *BssRelRo;
   static OutputSectionBase *Opd;
   static uint8_t *OpdBuf;
   static PhdrEntry *TlsPhdr;
@@ -252,6 +253,7 @@ template <class ELFT> uint64_t getHeader
 template <class ELFT> uint8_t Out<ELFT>::First;
 template <class ELFT> EhOutputSection<ELFT> *Out<ELFT>::EhFrame;
 template <class ELFT> OutputSection<ELFT> *Out<ELFT>::Bss;
+template <class ELFT> OutputSection<ELFT> *Out<ELFT>::BssRelRo;
 template <class ELFT> OutputSectionBase *Out<ELFT>::Opd;
 template <class ELFT> uint8_t *Out<ELFT>::OpdBuf;
 template <class ELFT> PhdrEntry *Out<ELFT>::TlsPhdr;

Modified: lld/trunk/ELF/Relocations.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Relocations.cpp?rev=291524&r1=291523&r2=291524&view=diff
==============================================================================
--- lld/trunk/ELF/Relocations.cpp (original)
+++ lld/trunk/ELF/Relocations.cpp Mon Jan  9 19:21:50 2017
@@ -399,7 +399,21 @@ template <class ELFT> static uint32_t ge
   return 1 << TrailingZeros;
 }
 
-// Reserve space in .bss for copy relocation.
+template <class ELFT> static bool isReadOnly(SharedSymbol<ELFT> *SS) {
+  typedef typename ELFT::uint uintX_t;
+  typedef typename ELFT::Phdr Elf_Phdr;
+
+  // Determine if the symbol is read-only by scanning the DSO's program headers.
+  uintX_t Value = SS->Sym.st_value;
+  for (const Elf_Phdr &Phdr : check(SS->file()->getObj().program_headers()))
+    if ((Phdr.p_type == ELF::PT_LOAD || Phdr.p_type == ELF::PT_GNU_RELRO) &&
+        !(Phdr.p_flags & ELF::PF_W) && Value >= Phdr.p_vaddr &&
+        Value < Phdr.p_vaddr + Phdr.p_memsz)
+      return true;
+  return false;
+}
+
+// Reserve space in .bss or .bss.rel.ro for copy relocation.
 template <class ELFT> static void addCopyRelSymbol(SharedSymbol<ELFT> *SS) {
   typedef typename ELFT::uint uintX_t;
   typedef typename ELFT::Sym Elf_Sym;
@@ -409,10 +423,16 @@ template <class ELFT> static void addCop
   if (SymSize == 0)
     fatal("cannot create a copy relocation for symbol " + toString(*SS));
 
+  // See if this symbol is in a read-only segment. If so, preserve the symbol's
+  // memory protection by reserving space in the .bss.rel.ro section.
+  bool IsReadOnly = isReadOnly(SS);
+  OutputSection<ELFT> *CopySec =
+      IsReadOnly ? Out<ELFT>::BssRelRo : Out<ELFT>::Bss;
+
   uintX_t Alignment = getAlignment(SS);
-  uintX_t Off = alignTo(Out<ELFT>::Bss->Size, Alignment);
-  Out<ELFT>::Bss->Size = Off + SymSize;
-  Out<ELFT>::Bss->updateAlignment(Alignment);
+  uintX_t Off = alignTo(CopySec->Size, Alignment);
+  CopySec->Size = Off + SymSize;
+  CopySec->updateAlignment(Alignment);
   uintX_t Shndx = SS->Sym.st_shndx;
   uintX_t Value = SS->Sym.st_value;
   // Look through the DSO's dynamic symbol table for aliases and create a
@@ -425,12 +445,12 @@ template <class ELFT> static void addCop
         Symtab<ELFT>::X->find(check(S.getName(SS->file()->getStringTable()))));
     if (!Alias)
       continue;
-    Alias->OffsetInBss = Off;
+    Alias->CopyIsInBssRelRo = IsReadOnly;
+    Alias->CopyOffset = Off;
     Alias->NeedsCopyOrPltAddr = true;
     Alias->symbol()->IsUsedInRegularObj = true;
   }
-  In<ELFT>::RelaDyn->addReloc(
-      {Target->CopyRel, Out<ELFT>::Bss, SS->OffsetInBss, false, SS, 0});
+  In<ELFT>::RelaDyn->addReloc({Target->CopyRel, CopySec, Off, false, SS, 0});
 }
 
 template <class ELFT>

Modified: lld/trunk/ELF/Symbols.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Symbols.cpp?rev=291524&r1=291523&r2=291524&view=diff
==============================================================================
--- lld/trunk/ELF/Symbols.cpp (original)
+++ lld/trunk/ELF/Symbols.cpp Mon Jan  9 19:21:50 2017
@@ -81,7 +81,7 @@ static typename ELFT::uint getSymVA(cons
       return 0;
     if (SS.isFunc())
       return Body.getPltVA<ELFT>();
-    return Out<ELFT>::Bss->Addr + SS.OffsetInBss;
+    return SS.getBssSectionForCopy()->Addr + SS.CopyOffset;
   }
   case SymbolBody::UndefinedKind:
     return 0;
@@ -97,7 +97,8 @@ SymbolBody::SymbolBody(Kind K, StringRef
                        uint8_t Type)
     : SymbolKind(K), NeedsCopyOrPltAddr(false), IsLocal(IsLocal),
       IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false),
-      IsInIgot(false), Type(Type), StOther(StOther), Name(Name) {}
+      IsInIgot(false), CopyIsInBssRelRo(false), Type(Type), StOther(StOther),
+      Name(Name) {}
 
 // Returns true if a symbol can be replaced at load-time by a symbol
 // with the same name defined in other ELF executable or DSO.
@@ -245,6 +246,12 @@ Undefined<ELFT>::Undefined(StringRefZ Na
   this->File = File;
 }
 
+template <typename ELFT>
+OutputSection<ELFT> *SharedSymbol<ELFT>::getBssSectionForCopy() const {
+  assert(needsCopy());
+  return CopyIsInBssRelRo ? Out<ELFT>::BssRelRo : Out<ELFT>::Bss;
+}
+
 DefinedCommon::DefinedCommon(StringRef Name, uint64_t Size, uint64_t Alignment,
                              uint8_t StOther, uint8_t Type, InputFile *File)
     : Defined(SymbolBody::DefinedCommonKind, Name, /*IsLocal=*/false, StOther,
@@ -366,6 +373,11 @@ template class elf::Undefined<ELF32BE>;
 template class elf::Undefined<ELF64LE>;
 template class elf::Undefined<ELF64BE>;
 
+template class elf::SharedSymbol<ELF32LE>;
+template class elf::SharedSymbol<ELF32BE>;
+template class elf::SharedSymbol<ELF64LE>;
+template class elf::SharedSymbol<ELF64BE>;
+
 template class elf::DefinedRegular<ELF32LE>;
 template class elf::DefinedRegular<ELF32BE>;
 template class elf::DefinedRegular<ELF64LE>;

Modified: lld/trunk/ELF/Symbols.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Symbols.h?rev=291524&r1=291523&r2=291524&view=diff
==============================================================================
--- lld/trunk/ELF/Symbols.h (original)
+++ lld/trunk/ELF/Symbols.h Mon Jan  9 19:21:50 2017
@@ -123,6 +123,11 @@ public:
   // True if this symbol is in the Igot sub-section of the .got.plt or .got.
   unsigned IsInIgot : 1;
 
+  // True if this is a shared symbol in a read-only segment which requires a
+  // copy relocation. This causes space for the symbol to be allocated in the
+  // .bss.rel.ro section.
+  unsigned CopyIsInBssRelRo : 1;
+
   // The following fields have the same meaning as the ELF symbol attributes.
   uint8_t Type;    // symbol type
   uint8_t StOther; // st_other field value
@@ -282,13 +287,15 @@ public:
   // This field is a pointer to the symbol's version definition.
   const Elf_Verdef *Verdef;
 
-  // OffsetInBss is significant only when needsCopy() is true.
-  uintX_t OffsetInBss = 0;
+  // CopyOffset is significant only when needsCopy() is true.
+  uintX_t CopyOffset = 0;
 
   // If non-null the symbol has a Thunk that may be used as an alternative
   // destination for callers of this Symbol.
   Thunk<ELFT> *ThunkData = nullptr;
   bool needsCopy() const { return this->NeedsCopyOrPltAddr && !this->isFunc(); }
+
+  OutputSection<ELFT> *getBssSectionForCopy() const;
 };
 
 // This class represents a symbol defined in an archive file. It is

Modified: lld/trunk/ELF/SyntheticSections.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/SyntheticSections.cpp?rev=291524&r1=291523&r2=291524&view=diff
==============================================================================
--- lld/trunk/ELF/SyntheticSections.cpp (original)
+++ lld/trunk/ELF/SyntheticSections.cpp Mon Jan  9 19:21:50 2017
@@ -1201,10 +1201,12 @@ SymbolTableSection<ELFT>::getOutputSecti
   }
   case SymbolBody::DefinedCommonKind:
     return In<ELFT>::Common->OutSec;
-  case SymbolBody::SharedKind:
-    if (cast<SharedSymbol<ELFT>>(Sym)->needsCopy())
-      return Out<ELFT>::Bss;
+  case SymbolBody::SharedKind: {
+    auto &SS = cast<SharedSymbol<ELFT>>(*Sym);
+    if (SS.needsCopy())
+      return SS.getBssSectionForCopy();
     break;
+  }
   case SymbolBody::UndefinedKind:
   case SymbolBody::LazyArchiveKind:
   case SymbolBody::LazyObjectKind:

Modified: lld/trunk/ELF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Writer.cpp?rev=291524&r1=291523&r2=291524&view=diff
==============================================================================
--- lld/trunk/ELF/Writer.cpp (original)
+++ lld/trunk/ELF/Writer.cpp Mon Jan  9 19:21:50 2017
@@ -250,6 +250,8 @@ template <class ELFT> void Writer<ELFT>:
   // Create singleton output sections.
   Out<ELFT>::Bss =
       make<OutputSection<ELFT>>(".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
+  Out<ELFT>::BssRelRo = make<OutputSection<ELFT>>(".bss.rel.ro", SHT_NOBITS,
+                                                  SHF_ALLOC | SHF_WRITE);
   In<ELFT>::DynStrTab = make<StringTableSection<ELFT>>(".dynstr", true);
   In<ELFT>::Dynamic = make<DynamicSection<ELFT>>();
   Out<ELFT>::EhFrame = make<EhOutputSection<ELFT>>();
@@ -498,6 +500,8 @@ template <class ELFT> bool elf::isRelroS
     return true;
   if (In<ELFT>::MipsGot && Sec == In<ELFT>::MipsGot->OutSec)
     return true;
+  if (Sec == Out<ELFT>::BssRelRo)
+    return true;
   StringRef S = Sec->getName();
   return S == ".data.rel.ro" || S == ".ctors" || S == ".dtors" || S == ".jcr" ||
          S == ".eh_frame" || S == ".openbsd.randomdata";
@@ -1079,6 +1083,8 @@ template <class ELFT> void Writer<ELFT>:
 template <class ELFT> void Writer<ELFT>::addPredefinedSections() {
   if (Out<ELFT>::Bss->Size > 0)
     OutputSections.push_back(Out<ELFT>::Bss);
+  if (Out<ELFT>::BssRelRo->Size > 0)
+    OutputSections.push_back(Out<ELFT>::BssRelRo);
 
   auto OS = dyn_cast_or_null<OutputSection<ELFT>>(findSection(".ARM.exidx"));
   if (OS && !OS->Sections.empty() && !Config->Relocatable)

Modified: lld/trunk/test/ELF/Inputs/copy-rel-pie.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/Inputs/copy-rel-pie.s?rev=291524&r1=291523&r2=291524&view=diff
==============================================================================
--- lld/trunk/test/ELF/Inputs/copy-rel-pie.s (original)
+++ lld/trunk/test/ELF/Inputs/copy-rel-pie.s Mon Jan  9 19:21:50 2017
@@ -1,9 +1,11 @@
+.data
 .global foo
 .type foo, @object
 .size foo, 4
 foo:
 .long 0
 
+.text
 .global bar
 .type bar, @function
 bar:

Added: lld/trunk/test/ELF/Inputs/relocation-copy-relro.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/Inputs/relocation-copy-relro.s?rev=291524&view=auto
==============================================================================
--- lld/trunk/test/ELF/Inputs/relocation-copy-relro.s (added)
+++ lld/trunk/test/ELF/Inputs/relocation-copy-relro.s Mon Jan  9 19:21:50 2017
@@ -0,0 +1,13 @@
+.rodata
+.globl a
+.size a, 4
+.type a, @object
+a:
+.word 1
+
+.section .data.rel.ro,"aw",%progbits
+.globl b
+.size b, 4
+.type b, @object
+b:
+.word 2

Added: lld/trunk/test/ELF/relocation-copy-relro.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/relocation-copy-relro.s?rev=291524&view=auto
==============================================================================
--- lld/trunk/test/ELF/relocation-copy-relro.s (added)
+++ lld/trunk/test/ELF/relocation-copy-relro.s Mon Jan  9 19:21:50 2017
@@ -0,0 +1,32 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/relocation-copy-relro.s -o %t2.o
+// RUN: ld.lld -shared %t2.o -o %t.so
+// RUN: ld.lld %t.o %t.so -o %t3
+// RUN: llvm-readobj -program-headers -s -r %t3 | FileCheck %s
+
+// CHECK:        Name: .bss.rel.ro (48)
+// CHECK-NEXT:   Type: SHT_NOBITS (0x8)
+// CHECK-NEXT:   Flags [ (0x3)
+// CHECK-NEXT:     SHF_ALLOC (0x2)
+// CHECK-NEXT:     SHF_WRITE (0x1)
+// CHECK-NEXT:   ]
+// CHECK-NEXT:   Address: 0x2020B0
+// CHECK-NEXT:   Offset: 0x20B0
+// CHECK-NEXT:   Size: 8
+
+// CHECK: 0x2020B0 R_X86_64_COPY a 0x0
+// CHECK: 0x2020B4 R_X86_64_COPY b 0x0
+
+// CHECK:      Type: PT_GNU_RELRO (0x6474E552)
+// CHECK-NEXT: Offset: 0x2000
+// CHECK-NEXT: VirtualAddress: 0x202000
+// CHECK-NEXT: PhysicalAddress: 0x202000
+// CHECK-NEXT: FileSize: 176
+// CHECK-NEXT: MemSize: 4096
+
+.text
+.global _start
+_start:
+movl $1, a
+movl $2, b




More information about the llvm-commits mailing list