aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Galenson <jgalenson@google.com>2018-08-24 15:21:58 +0000
committerStephen Hines <srhines@google.com>2018-09-09 23:30:29 -0700
commitf3f6b606e0575e2303936a6dd65ccb7ab98e4ca1 (patch)
tree4c612bd329c11dff75525696af7e3f4ecc43f8f5
parentadb14a3b06e4d40456aa83b7eefb527fa5fb7b75 (diff)
downloadllvm-f3f6b606e0575e2303936a6dd65ccb7ab98e4ca1.tar.gz
[cfi-verify] Support cross-DSO
When used in cross-DSO mode, CFI will generate calls to special functions rather than trap instructions. For example, instead of generating if (!InlinedFastCheck(f)) abort(); call *f CFI generates if (!InlinedFastCheck(f)) __cfi_slowpath(CallSiteTypeId, f); call *f This patch teaches cfi-verify to recognize calls to __cfi_slowpath and abort and treat them as trap functions. In addition to normal symbols, we also parse the dynamic relocations to handle cross-DSO calls in libraries. We also extend cfi-verify to recognize other patterns that occur using cross-DSO. For example, some indirect calls are not guarded by a branch to a trap but instead follow a call to __cfi_slowpath. For example: if (!InlinedFastCheck(f)) call *f else { __cfi_slowpath(CallSiteTypeId, f); call *f } In this case, the second call to f is not marked as protected by the current code. We thus recognize if indirect calls directly follow a call to a function that will trap on CFI violations and treat them as protected. We also ignore indirect calls in the PLT, since on AArch64 each entry contains an indirect call that should not be protected by CFI, and these are labeled incorrectly when debug information is not present. Differential Revision: https://reviews.llvm.org/D49383 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@340612 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--test/tools/llvm-cfi-verify/AArch64/Inputs/function-only-check.obin0 -> 68360 bytes
-rw-r--r--test/tools/llvm-cfi-verify/AArch64/function-only-check.s9
-rw-r--r--test/tools/llvm-cfi-verify/X86/Inputs/function-only-check.obin0 -> 15112 bytes
-rw-r--r--test/tools/llvm-cfi-verify/X86/function-only-check.s8
-rw-r--r--tools/llvm-cfi-verify/lib/FileAnalysis.cpp56
-rw-r--r--tools/llvm-cfi-verify/lib/FileAnalysis.h12
-rw-r--r--tools/llvm-cfi-verify/lib/GraphBuilder.cpp18
7 files changed, 102 insertions, 1 deletions
diff --git a/test/tools/llvm-cfi-verify/AArch64/Inputs/function-only-check.o b/test/tools/llvm-cfi-verify/AArch64/Inputs/function-only-check.o
new file mode 100644
index 00000000000..49142ff8170
--- /dev/null
+++ b/test/tools/llvm-cfi-verify/AArch64/Inputs/function-only-check.o
Binary files differ
diff --git a/test/tools/llvm-cfi-verify/AArch64/function-only-check.s b/test/tools/llvm-cfi-verify/AArch64/function-only-check.s
new file mode 100644
index 00000000000..30460c8cefc
--- /dev/null
+++ b/test/tools/llvm-cfi-verify/AArch64/function-only-check.s
@@ -0,0 +1,9 @@
+# RUN: llvm-cfi-verify -search-length-undef=6 %S/Inputs/function-only-check.o | FileCheck %s
+
+# CHECK-LABEL: {{^Instruction: .* \(PROTECTED\)}}
+# CHECK-NEXT: tiny.cc:9
+
+# CHECK: Expected Protected: 1 (100.00%)
+# CHECK: Unexpected Protected: 0 (0.00%)
+# CHECK: Expected Unprotected: 0 (0.00%)
+# CHECK: Unexpected Unprotected (BAD): 0 (0.00%)
diff --git a/test/tools/llvm-cfi-verify/X86/Inputs/function-only-check.o b/test/tools/llvm-cfi-verify/X86/Inputs/function-only-check.o
new file mode 100644
index 00000000000..099076a69ef
--- /dev/null
+++ b/test/tools/llvm-cfi-verify/X86/Inputs/function-only-check.o
Binary files differ
diff --git a/test/tools/llvm-cfi-verify/X86/function-only-check.s b/test/tools/llvm-cfi-verify/X86/function-only-check.s
new file mode 100644
index 00000000000..250f5dc5ab7
--- /dev/null
+++ b/test/tools/llvm-cfi-verify/X86/function-only-check.s
@@ -0,0 +1,8 @@
+# RUN: llvm-cfi-verify %S/Inputs/function-only-check.o | FileCheck %s
+
+# CHECK-LABEL: {{^Instruction: .* \(PROTECTED\)}}
+
+# CHECK: Expected Protected: 1 (100.00%)
+# CHECK: Unexpected Protected: 0 (0.00%)
+# CHECK: Expected Unprotected: 0 (0.00%)
+# CHECK: Unexpected Unprotected (BAD): 0 (0.00%)
diff --git a/tools/llvm-cfi-verify/lib/FileAnalysis.cpp b/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
index 29819d8d28e..8dc0802c465 100644
--- a/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
+++ b/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
@@ -105,6 +105,9 @@ Expected<FileAnalysis> FileAnalysis::Create(StringRef Filename) {
if (auto SectionParseResponse = Analysis.parseCodeSections())
return std::move(SectionParseResponse);
+ if (auto SymbolTableParseResponse = Analysis.parseSymbolTable())
+ return std::move(SymbolTableParseResponse);
+
return std::move(Analysis);
}
@@ -165,7 +168,18 @@ const Instr &FileAnalysis::getInstructionOrDie(uint64_t Address) const {
bool FileAnalysis::isCFITrap(const Instr &InstrMeta) const {
const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
- return InstrDesc.isTrap();
+ return InstrDesc.isTrap() || willTrapOnCFIViolation(InstrMeta);
+}
+
+bool FileAnalysis::willTrapOnCFIViolation(const Instr &InstrMeta) const {
+ const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
+ if (!InstrDesc.isCall())
+ return false;
+ uint64_t Target;
+ if (!MIA->evaluateBranch(InstrMeta.Instruction, InstrMeta.VMAddress,
+ InstrMeta.InstructionSize, Target))
+ return false;
+ return TrapOnFailFunctionAddresses.count(Target) > 0;
}
bool FileAnalysis::canFallThrough(const Instr &InstrMeta) const {
@@ -431,6 +445,12 @@ Error FileAnalysis::parseCodeSections() {
if (!(object::ELFSectionRef(Section).getFlags() & ELF::SHF_EXECINSTR))
continue;
+ // Avoid checking the PLT since it produces spurious failures on AArch64
+ // when ignoring DWARF data.
+ StringRef SectionName;
+ if (!Section.getName(SectionName) && SectionName == ".plt")
+ continue;
+
StringRef SectionContents;
if (Section.getContents(SectionContents))
return make_error<StringError>("Failed to retrieve section contents",
@@ -518,6 +538,40 @@ void FileAnalysis::addInstruction(const Instr &Instruction) {
}
}
+Error FileAnalysis::parseSymbolTable() {
+ // Functions that will trap on CFI violations.
+ SmallSet<StringRef, 4> TrapOnFailFunctions;
+ TrapOnFailFunctions.insert("__cfi_slowpath");
+ TrapOnFailFunctions.insert("__cfi_slowpath_diag");
+ TrapOnFailFunctions.insert("abort");
+
+ // Look through the list of symbols for functions that will trap on CFI
+ // violations.
+ for (auto &Sym : Object->symbols()) {
+ auto SymNameOrErr = Sym.getName();
+ if (!SymNameOrErr)
+ consumeError(SymNameOrErr.takeError());
+ else if (TrapOnFailFunctions.count(*SymNameOrErr) > 0) {
+ auto AddrOrErr = Sym.getAddress();
+ if (!AddrOrErr)
+ consumeError(AddrOrErr.takeError());
+ else
+ TrapOnFailFunctionAddresses.insert(*AddrOrErr);
+ }
+ }
+ if (auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Object)) {
+ for (const auto &Addr : ElfObject->getPltAddresses()) {
+ object::SymbolRef Sym(Addr.first, Object);
+ auto SymNameOrErr = Sym.getName();
+ if (!SymNameOrErr)
+ consumeError(SymNameOrErr.takeError());
+ else if (TrapOnFailFunctions.count(*SymNameOrErr) > 0)
+ TrapOnFailFunctionAddresses.insert(Addr.second);
+ }
+ }
+ return Error::success();
+}
+
UnsupportedDisassembly::UnsupportedDisassembly(StringRef Text) : Text(Text) {}
char UnsupportedDisassembly::ID;
diff --git a/tools/llvm-cfi-verify/lib/FileAnalysis.h b/tools/llvm-cfi-verify/lib/FileAnalysis.h
index 3f0a7048788..67421e80da3 100644
--- a/tools/llvm-cfi-verify/lib/FileAnalysis.h
+++ b/tools/llvm-cfi-verify/lib/FileAnalysis.h
@@ -11,6 +11,7 @@
#define LLVM_CFI_VERIFY_FILE_ANALYSIS_H
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallSet.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include "llvm/MC/MCAsmInfo.h"
@@ -110,6 +111,10 @@ public:
// Returns whether this instruction is used by CFI to trap the program.
bool isCFITrap(const Instr &InstrMeta) const;
+ // Returns whether this instruction is a call to a function that will trap on
+ // CFI violations (i.e., it serves as a trap in this instance).
+ bool willTrapOnCFIViolation(const Instr &InstrMeta) const;
+
// Returns whether this function can fall through to the next instruction.
// Undefined (and bad) instructions cannot fall through, and instruction that
// modify the control flow can only fall through if they are conditional
@@ -183,6 +188,10 @@ protected:
// internal members. Should only be called once by Create().
Error parseCodeSections();
+ // Parses the symbol table to look for the addresses of functions that will
+ // trap on CFI violations.
+ Error parseSymbolTable();
+
private:
// Members that describe the input file.
object::OwningBinary<object::Binary> Binary;
@@ -218,6 +227,9 @@ private:
// A list of addresses of indirect control flow instructions.
std::set<uint64_t> IndirectInstructions;
+
+ // The addresses of functions that will trap on CFI violations.
+ SmallSet<uint64_t, 4> TrapOnFailFunctionAddresses;
};
class UnsupportedDisassembly : public ErrorInfo<UnsupportedDisassembly> {
diff --git a/tools/llvm-cfi-verify/lib/GraphBuilder.cpp b/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
index 4153b5f6844..5b2bc6f0c3b 100644
--- a/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
+++ b/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
@@ -311,6 +311,24 @@ void GraphBuilder::buildFlowGraphImpl(const FileAnalysis &Analysis,
Result.ConditionalBranchNodes.push_back(BranchNode);
}
+ // When using cross-DSO, some indirect calls are not guarded by a branch to a
+ // trap but instead follow a call to __cfi_slowpath. For example:
+ // if (!InlinedFastCheck(f))
+ // call *f
+ // else {
+ // __cfi_slowpath(CallSiteTypeId, f);
+ // call *f
+ // }
+ // To mark the second call as protected, we recognize indirect calls that
+ // directly follow calls to functions that will trap on CFI violations.
+ if (CFCrossRefs.empty()) {
+ const Instr *PrevInstr = Analysis.getPrevInstructionSequential(ChildMeta);
+ if (PrevInstr && Analysis.willTrapOnCFIViolation(*PrevInstr)) {
+ Result.IntermediateNodes[PrevInstr->VMAddress] = Address;
+ HasValidCrossRef = true;
+ }
+ }
+
if (!HasValidCrossRef)
Result.OrphanedNodes.push_back(Address);