Skip to content
This repository was archived by the owner on Feb 5, 2019. It is now read-only.

Commit ff323c3

Browse files
committed
[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
1 parent e47abe7 commit ff323c3

File tree

7 files changed

+102
-1
lines changed

7 files changed

+102
-1
lines changed
Binary file not shown.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# RUN: llvm-cfi-verify -search-length-undef=6 %S/Inputs/function-only-check.o | FileCheck %s
2+
3+
# CHECK-LABEL: {{^Instruction: .* \(PROTECTED\)}}
4+
# CHECK-NEXT: tiny.cc:9
5+
6+
# CHECK: Expected Protected: 1 (100.00%)
7+
# CHECK: Unexpected Protected: 0 (0.00%)
8+
# CHECK: Expected Unprotected: 0 (0.00%)
9+
# CHECK: Unexpected Unprotected (BAD): 0 (0.00%)
Binary file not shown.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# RUN: llvm-cfi-verify %S/Inputs/function-only-check.o | FileCheck %s
2+
3+
# CHECK-LABEL: {{^Instruction: .* \(PROTECTED\)}}
4+
5+
# CHECK: Expected Protected: 1 (100.00%)
6+
# CHECK: Unexpected Protected: 0 (0.00%)
7+
# CHECK: Expected Unprotected: 0 (0.00%)
8+
# CHECK: Unexpected Unprotected (BAD): 0 (0.00%)

tools/llvm-cfi-verify/lib/FileAnalysis.cpp

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ Expected<FileAnalysis> FileAnalysis::Create(StringRef Filename) {
105105
if (auto SectionParseResponse = Analysis.parseCodeSections())
106106
return std::move(SectionParseResponse);
107107

108+
if (auto SymbolTableParseResponse = Analysis.parseSymbolTable())
109+
return std::move(SymbolTableParseResponse);
110+
108111
return std::move(Analysis);
109112
}
110113

@@ -165,7 +168,18 @@ const Instr &FileAnalysis::getInstructionOrDie(uint64_t Address) const {
165168

166169
bool FileAnalysis::isCFITrap(const Instr &InstrMeta) const {
167170
const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
168-
return InstrDesc.isTrap();
171+
return InstrDesc.isTrap() || willTrapOnCFIViolation(InstrMeta);
172+
}
173+
174+
bool FileAnalysis::willTrapOnCFIViolation(const Instr &InstrMeta) const {
175+
const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
176+
if (!InstrDesc.isCall())
177+
return false;
178+
uint64_t Target;
179+
if (!MIA->evaluateBranch(InstrMeta.Instruction, InstrMeta.VMAddress,
180+
InstrMeta.InstructionSize, Target))
181+
return false;
182+
return TrapOnFailFunctionAddresses.count(Target) > 0;
169183
}
170184

171185
bool FileAnalysis::canFallThrough(const Instr &InstrMeta) const {
@@ -431,6 +445,12 @@ Error FileAnalysis::parseCodeSections() {
431445
if (!(object::ELFSectionRef(Section).getFlags() & ELF::SHF_EXECINSTR))
432446
continue;
433447

448+
// Avoid checking the PLT since it produces spurious failures on AArch64
449+
// when ignoring DWARF data.
450+
StringRef SectionName;
451+
if (!Section.getName(SectionName) && SectionName == ".plt")
452+
continue;
453+
434454
StringRef SectionContents;
435455
if (Section.getContents(SectionContents))
436456
return make_error<StringError>("Failed to retrieve section contents",
@@ -518,6 +538,40 @@ void FileAnalysis::addInstruction(const Instr &Instruction) {
518538
}
519539
}
520540

541+
Error FileAnalysis::parseSymbolTable() {
542+
// Functions that will trap on CFI violations.
543+
SmallSet<StringRef, 4> TrapOnFailFunctions;
544+
TrapOnFailFunctions.insert("__cfi_slowpath");
545+
TrapOnFailFunctions.insert("__cfi_slowpath_diag");
546+
TrapOnFailFunctions.insert("abort");
547+
548+
// Look through the list of symbols for functions that will trap on CFI
549+
// violations.
550+
for (auto &Sym : Object->symbols()) {
551+
auto SymNameOrErr = Sym.getName();
552+
if (!SymNameOrErr)
553+
consumeError(SymNameOrErr.takeError());
554+
else if (TrapOnFailFunctions.count(*SymNameOrErr) > 0) {
555+
auto AddrOrErr = Sym.getAddress();
556+
if (!AddrOrErr)
557+
consumeError(AddrOrErr.takeError());
558+
else
559+
TrapOnFailFunctionAddresses.insert(*AddrOrErr);
560+
}
561+
}
562+
if (auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Object)) {
563+
for (const auto &Addr : ElfObject->getPltAddresses()) {
564+
object::SymbolRef Sym(Addr.first, Object);
565+
auto SymNameOrErr = Sym.getName();
566+
if (!SymNameOrErr)
567+
consumeError(SymNameOrErr.takeError());
568+
else if (TrapOnFailFunctions.count(*SymNameOrErr) > 0)
569+
TrapOnFailFunctionAddresses.insert(Addr.second);
570+
}
571+
}
572+
return Error::success();
573+
}
574+
521575
UnsupportedDisassembly::UnsupportedDisassembly(StringRef Text) : Text(Text) {}
522576

523577
char UnsupportedDisassembly::ID;

tools/llvm-cfi-verify/lib/FileAnalysis.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define LLVM_CFI_VERIFY_FILE_ANALYSIS_H
1212

1313
#include "llvm/ADT/DenseMap.h"
14+
#include "llvm/ADT/SmallSet.h"
1415
#include "llvm/BinaryFormat/ELF.h"
1516
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
1617
#include "llvm/MC/MCAsmInfo.h"
@@ -110,6 +111,10 @@ class FileAnalysis {
110111
// Returns whether this instruction is used by CFI to trap the program.
111112
bool isCFITrap(const Instr &InstrMeta) const;
112113

114+
// Returns whether this instruction is a call to a function that will trap on
115+
// CFI violations (i.e., it serves as a trap in this instance).
116+
bool willTrapOnCFIViolation(const Instr &InstrMeta) const;
117+
113118
// Returns whether this function can fall through to the next instruction.
114119
// Undefined (and bad) instructions cannot fall through, and instruction that
115120
// modify the control flow can only fall through if they are conditional
@@ -183,6 +188,10 @@ class FileAnalysis {
183188
// internal members. Should only be called once by Create().
184189
Error parseCodeSections();
185190

191+
// Parses the symbol table to look for the addresses of functions that will
192+
// trap on CFI violations.
193+
Error parseSymbolTable();
194+
186195
private:
187196
// Members that describe the input file.
188197
object::OwningBinary<object::Binary> Binary;
@@ -218,6 +227,9 @@ class FileAnalysis {
218227

219228
// A list of addresses of indirect control flow instructions.
220229
std::set<uint64_t> IndirectInstructions;
230+
231+
// The addresses of functions that will trap on CFI violations.
232+
SmallSet<uint64_t, 4> TrapOnFailFunctionAddresses;
221233
};
222234

223235
class UnsupportedDisassembly : public ErrorInfo<UnsupportedDisassembly> {

tools/llvm-cfi-verify/lib/GraphBuilder.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,24 @@ void GraphBuilder::buildFlowGraphImpl(const FileAnalysis &Analysis,
311311
Result.ConditionalBranchNodes.push_back(BranchNode);
312312
}
313313

314+
// When using cross-DSO, some indirect calls are not guarded by a branch to a
315+
// trap but instead follow a call to __cfi_slowpath. For example:
316+
// if (!InlinedFastCheck(f))
317+
// call *f
318+
// else {
319+
// __cfi_slowpath(CallSiteTypeId, f);
320+
// call *f
321+
// }
322+
// To mark the second call as protected, we recognize indirect calls that
323+
// directly follow calls to functions that will trap on CFI violations.
324+
if (CFCrossRefs.empty()) {
325+
const Instr *PrevInstr = Analysis.getPrevInstructionSequential(ChildMeta);
326+
if (PrevInstr && Analysis.willTrapOnCFIViolation(*PrevInstr)) {
327+
Result.IntermediateNodes[PrevInstr->VMAddress] = Address;
328+
HasValidCrossRef = true;
329+
}
330+
}
331+
314332
if (!HasValidCrossRef)
315333
Result.OrphanedNodes.push_back(Address);
316334

0 commit comments

Comments
 (0)