[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