[lld] r325047 - [LLD] Implement /guard:[no]longjmp

Reid Kleckner via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 13 12:32:53 PST 2018


Author: rnk
Date: Tue Feb 13 12:32:53 2018
New Revision: 325047

URL: http://llvm.org/viewvc/llvm-project?rev=325047&view=rev
Log:
[LLD] Implement /guard:[no]longjmp

Summary:
This protects calls to longjmp from transferring control to arbitrary
program points. Instead, longjmp calls are limited to the set of
registered setjmp return addresses.

This also implements /guard:nolongjmp to allow users to link in object
files that call setjmp that weren't compiled with /guard:cf. In this
case, the linker will approximate the set of address taken functions,
but it will leave longjmp unprotected.

I used the following program to test, compiling it with different -guard
flags:
  $ cl -c t.c -guard:cf
  $ lld-link t.obj -guard:cf

  #include <setjmp.h>
  #include <stdio.h>
  jmp_buf buf;
  void g() {
    printf("before longjmp\n");
    fflush(stdout);
    longjmp(buf, 1);
  }
  void f() {
    if (setjmp(buf)) {
      printf("setjmp returned non-zero\n");
      return;
    }
    g();
  }
  int main() {
    f();
    printf("hello world\n");
  }

In particular, the program aborts when the code is compiled *without*
-guard:cf and linked with -guard:cf. That indicates that longjmps are
protected.

Reviewers: ruiu, inglorion, amccarth

Subscribers: llvm-commits

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

Added:
    lld/trunk/test/COFF/guard-longjmp.s
Modified:
    lld/trunk/COFF/Config.h
    lld/trunk/COFF/DriverUtils.cpp
    lld/trunk/COFF/InputFiles.cpp
    lld/trunk/COFF/InputFiles.h
    lld/trunk/COFF/Writer.cpp
    lld/trunk/test/COFF/gfids-corrupt.s
    lld/trunk/test/COFF/gfids-fallback.s
    lld/trunk/test/COFF/gfids-gc.s
    lld/trunk/test/COFF/gfids-icf.s

Modified: lld/trunk/COFF/Config.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Config.h?rev=325047&r1=325046&r2=325047&view=diff
==============================================================================
--- lld/trunk/COFF/Config.h (original)
+++ lld/trunk/COFF/Config.h Tue Feb 13 12:32:53 2018
@@ -72,6 +72,12 @@ enum class DebugType {
   Fixup = 0x4,  /// Relocation Table
 };
 
+enum class GuardCFLevel {
+  Off,
+  NoLongJmp, // Emit gfids but no longjmp tables
+  Full,      // Enable all protections.
+};
+
 // Global configuration.
 struct Configuration {
   enum ManifestKind { SideBySide, Embed, No };
@@ -113,7 +119,7 @@ struct Configuration {
   bool SaveTemps = false;
 
   // /guard:cf
-  bool GuardCF;
+  GuardCFLevel GuardCF = GuardCFLevel::Off;
 
   // Used for SafeSEH.
   Symbol *SEHTable = nullptr;

Modified: lld/trunk/COFF/DriverUtils.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/DriverUtils.cpp?rev=325047&r1=325046&r2=325047&view=diff
==============================================================================
--- lld/trunk/COFF/DriverUtils.cpp (original)
+++ lld/trunk/COFF/DriverUtils.cpp Tue Feb 13 12:32:53 2018
@@ -128,13 +128,19 @@ void parseVersion(StringRef Arg, uint32_
     fatal("invalid number: " + S2);
 }
 
-void parseGuard(StringRef Arg) {
-  if (Arg.equals_lower("no"))
-    Config->GuardCF = false;
-  else if (Arg.equals_lower("cf"))
-    Config->GuardCF = true;
-  else
-    fatal("invalid argument to /GUARD: " + Arg);
+void parseGuard(StringRef FullArg) {
+  SmallVector<StringRef, 1> SplitArgs;
+  FullArg.split(SplitArgs, ",");
+  for (StringRef Arg : SplitArgs) {
+    if (Arg.equals_lower("no"))
+      Config->GuardCF = GuardCFLevel::Off;
+    else if (Arg.equals_lower("nolongjmp"))
+      Config->GuardCF = GuardCFLevel::NoLongJmp;
+    else if (Arg.equals_lower("cf") || Arg.equals_lower("longjmp"))
+      Config->GuardCF = GuardCFLevel::Full;
+    else
+      fatal("invalid argument to /GUARD: " + Arg);
+  }
 }
 
 // Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".

Modified: lld/trunk/COFF/InputFiles.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/InputFiles.cpp?rev=325047&r1=325046&r2=325047&view=diff
==============================================================================
--- lld/trunk/COFF/InputFiles.cpp (original)
+++ lld/trunk/COFF/InputFiles.cpp Tue Feb 13 12:32:53 2018
@@ -183,8 +183,10 @@ SectionChunk *ObjFile::readSection(uint3
   // linked in the regular manner.
   if (C->isCodeView())
     DebugChunks.push_back(C);
-  else if (Config->GuardCF && Name == ".gfids$y")
+  else if (Config->GuardCF != GuardCFLevel::Off && Name == ".gfids$y")
     GuardFidChunks.push_back(C);
+  else if (Config->GuardCF != GuardCFLevel::Off && Name == ".gljmp$y")
+    GuardLJmpChunks.push_back(C);
   else if (Name == ".sxdata")
     SXDataChunks.push_back(C);
   else

Modified: lld/trunk/COFF/InputFiles.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/InputFiles.h?rev=325047&r1=325046&r2=325047&view=diff
==============================================================================
--- lld/trunk/COFF/InputFiles.h (original)
+++ lld/trunk/COFF/InputFiles.h Tue Feb 13 12:32:53 2018
@@ -112,6 +112,7 @@ public:
   ArrayRef<SectionChunk *> getDebugChunks() { return DebugChunks; }
   ArrayRef<SectionChunk *> getSXDataChunks() { return SXDataChunks; }
   ArrayRef<SectionChunk *> getGuardFidChunks() { return GuardFidChunks; }
+  ArrayRef<SectionChunk *> getGuardLJmpChunks() { return GuardLJmpChunks; }
   ArrayRef<Symbol *> getSymbols() { return Symbols; }
 
   // Returns a Symbol object for the SymbolIndex'th symbol in the
@@ -175,9 +176,10 @@ private:
   // 32-bit x86.
   std::vector<SectionChunk *> SXDataChunks;
 
-  // Chunks containing symbol table indices of address taken symbols.  These are
-  // not linked into the final binary when /guard:cf is set.
+  // Chunks containing symbol table indices of address taken symbols and longjmp
+  // targets.  These are not linked into the final binary when /guard:cf is set.
   std::vector<SectionChunk *> GuardFidChunks;
+  std::vector<SectionChunk *> GuardLJmpChunks;
 
   // This vector contains the same chunks as Chunks, but they are
   // indexed such that you can get a SectionChunk by section index.

Modified: lld/trunk/COFF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Writer.cpp?rev=325047&r1=325046&r2=325047&view=diff
==============================================================================
--- lld/trunk/COFF/Writer.cpp (original)
+++ lld/trunk/COFF/Writer.cpp Tue Feb 13 12:32:53 2018
@@ -124,7 +124,8 @@ private:
   void openFile(StringRef OutputPath);
   template <typename PEHeaderTy> void writeHeader();
   void createSEHTable(OutputSection *RData);
-  void createGFIDTable(OutputSection *RData);
+  void createGuardCFTables(OutputSection *RData);
+  void createGLJmpTable(OutputSection *RData);
   void markSymbolsForRVATable(ObjFile *File,
                               ArrayRef<SectionChunk *> SymIdxChunks,
                               SymbolRVASet &TableSymbols);
@@ -440,9 +441,9 @@ void Writer::createMiscChunks() {
   if (Config->Machine == I386)
     createSEHTable(RData);
 
-  // Create the guard function id table if requested.
-  if (Config->GuardCF)
-    createGFIDTable(RData);
+  // Create /guard:cf tables if requested.
+  if (Config->GuardCF != GuardCFLevel::Off)
+    createGuardCFTables(RData);
 }
 
 // Create .idata section for the DLL-imported symbol table.
@@ -734,7 +735,7 @@ template <typename PEHeaderTy> void Writ
     PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
   if (!Config->AllowIsolation)
     PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
-  if (Config->GuardCF)
+  if (Config->GuardCF != GuardCFLevel::Off)
     PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF;
   if (Config->Machine == I386 && !SEHTable &&
       !Symtab->findUnderscore("_load_config_used"))
@@ -896,17 +897,21 @@ static void markSymbolsWithRelocations(O
 // Create the guard function id table. This is a table of RVAs of all
 // address-taken functions. It is sorted and uniqued, just like the safe SEH
 // table.
-void Writer::createGFIDTable(OutputSection *RData) {
+void Writer::createGuardCFTables(OutputSection *RData) {
   SymbolRVASet AddressTakenSyms;
+  SymbolRVASet LongJmpTargets;
   for (ObjFile *File : ObjFile::Instances) {
-    // If the object was compiled with /guard:cf, the address taken symbols are
-    // in the .gfids$y sections. Otherwise, we approximate the set of address
-    // taken symbols by checking which symbols were used by relocations in live
-    // sections.
-    if (File->hasGuardCF())
+    // If the object was compiled with /guard:cf, the address taken symbols
+    // are in .gfids$y sections, and the longjmp targets are in .gljmp$y
+    // sections. If the object was not compiled with /guard:cf, we assume there
+    // were no setjmp targets, and that all code symbols with relocations are
+    // possibly address-taken.
+    if (File->hasGuardCF()) {
       markSymbolsForRVATable(File, File->getGuardFidChunks(), AddressTakenSyms);
-    else
+      markSymbolsForRVATable(File, File->getGuardLJmpChunks(), LongJmpTargets);
+    } else {
       markSymbolsWithRelocations(File, AddressTakenSyms);
+    }
   }
 
   // Mark the image entry as address-taken.
@@ -916,10 +921,17 @@ void Writer::createGFIDTable(OutputSecti
   maybeAddRVATable(RData, std::move(AddressTakenSyms), "__guard_fids_table",
                    "__guard_fids_count");
 
+  // Add the longjmp target table unless the user told us not to.
+  if (Config->GuardCF == GuardCFLevel::Full)
+    maybeAddRVATable(RData, std::move(LongJmpTargets), "__guard_longjmp_table",
+                     "__guard_longjmp_count");
+
   // Set __guard_flags, which will be used in the load config to indicate that
   // /guard:cf was enabled.
   uint32_t GuardFlags = uint32_t(coff_guard_flags::CFInstrumented) |
                         uint32_t(coff_guard_flags::HasFidTable);
+  if (Config->GuardCF == GuardCFLevel::Full)
+    GuardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable);
   Symbol *FlagSym = Symtab->findUnderscore("__guard_flags");
   cast<DefinedAbsolute>(FlagSym)->setVA(GuardFlags);
 }

Modified: lld/trunk/test/COFF/gfids-corrupt.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/gfids-corrupt.s?rev=325047&r1=325046&r2=325047&view=diff
==============================================================================
--- lld/trunk/test/COFF/gfids-corrupt.s (original)
+++ lld/trunk/test/COFF/gfids-corrupt.s Tue Feb 13 12:32:53 2018
@@ -1,5 +1,5 @@
 # RUN: llvm-mc -triple x86_64-windows-msvc %s -filetype=obj -o %t.obj
-# RUN: lld-link %t.obj -opt:noref -guard:cf -out:%t.exe -entry:main 2>&1 | FileCheck %s --check-prefix=ERRS
+# RUN: lld-link %t.obj -opt:noref -guard:nolongjmp -out:%t.exe -entry:main 2>&1 | FileCheck %s --check-prefix=ERRS
 # RUN: llvm-readobj -file-headers -coff-load-config %t.exe | FileCheck %s
 
 # ERRS: warning: ignoring .gfids$y symbol table index section in object {{.*}}gfids-corrupt{{.*}}

Modified: lld/trunk/test/COFF/gfids-fallback.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/gfids-fallback.s?rev=325047&r1=325046&r2=325047&view=diff
==============================================================================
--- lld/trunk/test/COFF/gfids-fallback.s (original)
+++ lld/trunk/test/COFF/gfids-fallback.s Tue Feb 13 12:32:53 2018
@@ -1,6 +1,6 @@
 # RUN: grep -B99999 [S]PLITMARKER %s | llvm-mc -triple x86_64-windows-msvc -filetype=obj -o %t1.obj
 # RUN: grep -A99999 [S]PLITMARKER %s | llvm-mc -triple x86_64-windows-msvc -filetype=obj -o %t2.obj
-# RUN: lld-link %t1.obj %t2.obj -guard:cf -out:%t.exe -entry:main -opt:noref
+# RUN: lld-link %t1.obj %t2.obj -guard:nolongjmp -out:%t.exe -entry:main -opt:noref
 # RUN: llvm-readobj -file-headers -coff-load-config %t.exe | FileCheck %s
 
 # CHECK: ImageBase: 0x140000000

Modified: lld/trunk/test/COFF/gfids-gc.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/gfids-gc.s?rev=325047&r1=325046&r2=325047&view=diff
==============================================================================
--- lld/trunk/test/COFF/gfids-gc.s (original)
+++ lld/trunk/test/COFF/gfids-gc.s Tue Feb 13 12:32:53 2018
@@ -1,7 +1,7 @@
 # RUN: llvm-mc -triple x86_64-windows-msvc %s -filetype=obj -o %t.obj
-# RUN: lld-link %t.obj -guard:cf -out:%t.exe -opt:noref -entry:main
+# RUN: lld-link %t.obj -guard:nolongjmp -out:%t.exe -opt:noref -entry:main
 # RUN: llvm-readobj -file-headers -coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK-NOGC
-# RUN: lld-link %t.obj -guard:cf -out:%t.exe -opt:ref -entry:main
+# RUN: lld-link %t.obj -guard:nolongjmp -out:%t.exe -opt:ref -entry:main
 # RUN: llvm-readobj -file-headers -coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK-GC
 
 # This assembly is meant to mimic what CL emits for this kind of C code when

Modified: lld/trunk/test/COFF/gfids-icf.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/gfids-icf.s?rev=325047&r1=325046&r2=325047&view=diff
==============================================================================
--- lld/trunk/test/COFF/gfids-icf.s (original)
+++ lld/trunk/test/COFF/gfids-icf.s Tue Feb 13 12:32:53 2018
@@ -1,5 +1,5 @@
 # RUN: llvm-mc -triple x86_64-windows-msvc %s -filetype=obj -o %t.obj
-# RUN: lld-link %t.obj -guard:cf -out:%t.exe -opt:icf -entry:main
+# RUN: lld-link %t.obj -guard:nolongjmp -out:%t.exe -opt:icf -entry:main
 # RUN: llvm-readobj -file-headers -coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK
 
 # This assembly is meant to mimic what CL emits for this kind of C code:

Added: lld/trunk/test/COFF/guard-longjmp.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/guard-longjmp.s?rev=325047&view=auto
==============================================================================
--- lld/trunk/test/COFF/guard-longjmp.s (added)
+++ lld/trunk/test/COFF/guard-longjmp.s Tue Feb 13 12:32:53 2018
@@ -0,0 +1,103 @@
+# RUN: llvm-mc -triple x86_64-windows-msvc %s -filetype=obj -o %t.obj
+# RUN: lld-link %t.obj -guard:cf -out:%t.exe -entry:main
+# RUN: llvm-readobj -file-headers -coff-load-config %t.exe | FileCheck %s
+
+# CHECK: ImageBase: 0x140000000
+# CHECK: LoadConfig [
+# CHECK:   SEHandlerTable: 0x0
+# CHECK:   SEHandlerCount: 0
+# CHECK:   GuardCFCheckFunction: 0x0
+# CHECK:   GuardCFCheckDispatch: 0x0
+# CHECK:   GuardCFFunctionTable: 0x14000{{.*}}
+# CHECK:   GuardCFFunctionCount: 1
+# CHECK:   GuardFlags: 0x10500
+# CHECK:   GuardAddressTakenIatEntryTable: 0x0
+# CHECK:   GuardAddressTakenIatEntryCount: 0
+# CHECK:   GuardLongJumpTargetTable: 0x14000{{.*}}
+# CHECK:   GuardLongJumpTargetCount: 1
+# CHECK: ]
+# CHECK:      GuardLJmpTable [
+# CHECK-NEXT:   0x14000{{.*}}
+# CHECK-NEXT: ]
+
+
+# This assembly is reduced from C code like:
+# #include <setjmp.h>
+# jmp_buf buf;
+# void g() { longjmp(buf, 1); }
+# void f() {
+#   if (setjmp(buf))
+#     return;
+#   g();
+# }
+# int main() { f(); }
+
+# We need @feat.00 to have 0x800 to indicate /guard:cf.
+        .def     @feat.00;
+        .scl    3;
+        .type   0;
+        .endef
+        .globl  @feat.00
+ at feat.00 = 0x801
+        .def     f; .scl    2; .type   32; .endef
+        .globl  f
+f:
+        pushq   %rbp
+        subq    $32, %rsp
+        leaq    32(%rsp), %rbp
+        leaq    buf(%rip), %rcx
+        leaq    -32(%rbp), %rdx
+        callq   _setjmp
+.Lljmp1:
+        testl   %eax, %eax
+        je      .LBB1_1
+        addq    $32, %rsp
+        popq    %rbp
+        retq
+.LBB1_1:                                # %if.end
+        leaq    buf(%rip), %rcx
+        movl    $1, %edx
+        callq   longjmp
+        ud2
+
+        # Record the longjmp target.
+        .section        .gljmp$y,"dr"
+        .symidx .Lljmp1
+        .text
+
+        # Provide setjmp/longjmp stubs.
+        .def     _setjmp; .scl    2; .type   32; .endef
+        .globl  _setjmp
+_setjmp:
+        retq
+
+        .def     longjmp; .scl    2; .type   32; .endef
+        .globl  longjmp
+longjmp:
+        retq
+
+        .def     main; .scl    2; .type   32; .endef
+        .globl  main                    # -- Begin function main
+main:                                   # @main
+        subq    $40, %rsp
+        callq   f
+        xorl    %eax, %eax
+        addq    $40, %rsp
+        retq
+
+        .comm   buf,256,4               # @buf
+
+        .section .rdata,"dr"
+.globl _load_config_used
+_load_config_used:
+        .long 256
+        .fill 124, 1, 0
+        .quad __guard_fids_table
+        .quad __guard_fids_count
+        .long __guard_flags
+        .fill 12, 1, 0
+        .quad __guard_iat_table
+        .quad __guard_iat_count
+        .quad __guard_longjmp_table
+        .quad __guard_fids_count
+        .fill 84, 1, 0




More information about the llvm-commits mailing list