From b2c3ea760320443f9d4360cbd7fcec1a8b3cc28e Mon Sep 17 00:00:00 2001 From: Vlad Tsyrklevich Date: Thu, 1 Feb 2018 23:45:18 +0000 Subject: [PATCH] [cfi-verify] Add blame context printing, and improved print format. Summary: This update now allows users to specify `--blame-context` and `--blame-context-all` to print source file blame information for the source of the blame. Also updates the inline printing to correctly identify the top of the inlining stack for blame information. Patch by Mitch Phillips! Reviewers: vlad.tsyrklevich Subscribers: llvm-commits, kcc, pcc Differential Revision: https://reviews.llvm.org/D40111 llvm-svn: 324035 --- .../X86/blacklist-expected-unprotected.s | 2 +- .../llvm-cfi-verify/X86/blacklist-match-fun.s | 2 +- .../X86/blacklist-unexpected-protected.s | 2 +- .../tools/llvm-cfi-verify/X86/dot-printing.s | 5 +- .../X86/indirect-cf-elimination.s | 4 +- .../llvm-cfi-verify/X86/protected-lineinfo.s | 2 +- .../X86/unprotected-lineinfo.s | 2 +- .../tools/llvm-cfi-verify/llvm-cfi-verify.cpp | 152 +++++++++++++----- 8 files changed, 125 insertions(+), 46 deletions(-) diff --git a/llvm/test/tools/llvm-cfi-verify/X86/blacklist-expected-unprotected.s b/llvm/test/tools/llvm-cfi-verify/X86/blacklist-expected-unprotected.s index c7a3daa0776cd9..dccbd69181216f 100644 --- a/llvm/test/tools/llvm-cfi-verify/X86/blacklist-expected-unprotected.s +++ b/llvm/test/tools/llvm-cfi-verify/X86/blacklist-expected-unprotected.s @@ -3,7 +3,7 @@ # RUN: echo "src:*tiny*" > %t.blacklist.txt # RUN: llvm-cfi-verify %t.o %t.blacklist.txt | FileCheck %s -# CHECK-LABEL: U +# CHECK-LABEL: {{^Instruction: .* \(FAIL_BAD_CONDITIONAL_BRANCH\)}} # CHECK-NEXT: tiny.cc:11 # CHECK-NEXT: {{^Blacklist Match:.*blacklist\.txt:1$}} # CHECK-NEXT: ====> Expected Unprotected diff --git a/llvm/test/tools/llvm-cfi-verify/X86/blacklist-match-fun.s b/llvm/test/tools/llvm-cfi-verify/X86/blacklist-match-fun.s index f46fb926c5fa29..21e1ffe7c5c973 100644 --- a/llvm/test/tools/llvm-cfi-verify/X86/blacklist-match-fun.s +++ b/llvm/test/tools/llvm-cfi-verify/X86/blacklist-match-fun.s @@ -3,7 +3,7 @@ # RUN: echo "fun:*main*" > %t.blacklist.txt # RUN: llvm-cfi-verify %t.o %t.blacklist.txt | FileCheck %s -# CHECK-LABEL: U +# CHECK-LABEL: {{^Instruction: .* \(FAIL_BAD_CONDITIONAL_BRANCH\)}} # CHECK-NEXT: tiny.cc:11 # CHECK-NEXT: {{^Blacklist Match:.*blacklist\.txt:1$}} # CHECK-NEXT: ====> Expected Unprotected diff --git a/llvm/test/tools/llvm-cfi-verify/X86/blacklist-unexpected-protected.s b/llvm/test/tools/llvm-cfi-verify/X86/blacklist-unexpected-protected.s index 0fd412416a920b..2a32e78c8df626 100644 --- a/llvm/test/tools/llvm-cfi-verify/X86/blacklist-unexpected-protected.s +++ b/llvm/test/tools/llvm-cfi-verify/X86/blacklist-unexpected-protected.s @@ -3,7 +3,7 @@ # RUN: echo "src:*tiny*" > %t.blacklist.txt # RUN: llvm-cfi-verify %t.o %t.blacklist.txt | FileCheck %s -# CHECK-LABEL: P +# CHECK-LABEL: {{^Instruction: .* \(PROTECTED\)}} # CHECK-NEXT: tiny.cc:11 # CHECK-NEXT: {{^Blacklist Match:.*blacklist\.txt:1$}} # CHECK-NEXT: ====> Unexpected Protected diff --git a/llvm/test/tools/llvm-cfi-verify/X86/dot-printing.s b/llvm/test/tools/llvm-cfi-verify/X86/dot-printing.s index 5e0a2090d8222a..789f1727e8913b 100644 --- a/llvm/test/tools/llvm-cfi-verify/X86/dot-printing.s +++ b/llvm/test/tools/llvm-cfi-verify/X86/dot-printing.s @@ -3,14 +3,15 @@ # RUN: llvm-cfi-verify -print-graphs %t.o | FileCheck %s # The expected output is as follows: -# P 0x7b | callq *%rax +# ----------------- Begin Instruction ----------------- +# PROTECTED 0x7b: callq *%rax # digraph graph_0x7b { # "0x77: jbe 2" -> "0x7b: callq *%rax" # "0x77: jbe 2" -> "0x79: ud2" # } # 0x7b = tiny.cc:11:3 (main) -# CHECK: {{^P.*callq +\*%rax.*$}} +# CHECK-LABEL: {{^Instruction: .* \(PROTECTED\):.*callq +\*%rax}} # CHECK-NEXT: digraph # CHECK-NEXT: {{^.*jbe.*->.*callq \*%rax}} # CHECK-NEXT: {{^.*jbe.*->.*ud2}} diff --git a/llvm/test/tools/llvm-cfi-verify/X86/indirect-cf-elimination.s b/llvm/test/tools/llvm-cfi-verify/X86/indirect-cf-elimination.s index e9b873471cb1b9..be5217e02e8f68 100644 --- a/llvm/test/tools/llvm-cfi-verify/X86/indirect-cf-elimination.s +++ b/llvm/test/tools/llvm-cfi-verify/X86/indirect-cf-elimination.s @@ -1,5 +1,5 @@ # RUN: llvm-mc %s -filetype obj -triple x86_64-linux-elf -o %t.o -# RUN: llvm-cfi-verify %t.o 2>&1 | FileCheck %s +# RUN: llvm-cfi-verify %t.o 2>&1 --summarize | FileCheck %s # This is the same file as protected-lineinfo.s, however contains a hand- # assembled function (fake_function) that has no line table information @@ -10,6 +10,8 @@ # reporting of the cfi-verify program. It should only find a single indirect CF # instruction at `tiny.cc:11` (see protected-lineinfo.s for the source). +# CHECK-NOT: Begin Instruction + # CHECK: Expected Protected: 1 (100.00%) # CHECK: Unexpected Protected: 0 (0.00%) # CHECK: Expected Unprotected: 0 (0.00%) diff --git a/llvm/test/tools/llvm-cfi-verify/X86/protected-lineinfo.s b/llvm/test/tools/llvm-cfi-verify/X86/protected-lineinfo.s index 8eaf2e5e725bbf..f1d0be1ed963a6 100644 --- a/llvm/test/tools/llvm-cfi-verify/X86/protected-lineinfo.s +++ b/llvm/test/tools/llvm-cfi-verify/X86/protected-lineinfo.s @@ -2,7 +2,7 @@ # RUN: -triple x86_64-linux-elf -o %t.o # RUN: llvm-cfi-verify %t.o | FileCheck %s -# CHECK-LABEL: P +# CHECK-LABEL: {{^Instruction: .* \(PROTECTED\)}} # CHECK-NEXT: tiny.cc:11 # CHECK: Expected Protected: 1 (100.00%) diff --git a/llvm/test/tools/llvm-cfi-verify/X86/unprotected-lineinfo.s b/llvm/test/tools/llvm-cfi-verify/X86/unprotected-lineinfo.s index 65782cb5e4208a..687ceb558e5a0d 100644 --- a/llvm/test/tools/llvm-cfi-verify/X86/unprotected-lineinfo.s +++ b/llvm/test/tools/llvm-cfi-verify/X86/unprotected-lineinfo.s @@ -2,7 +2,7 @@ # RUN: -triple x86_64-linux-elf -o %t.o # RUN: llvm-cfi-verify %t.o | FileCheck %s -# CHECK-LABEL: U +# CHECK-LABEL: {{^Instruction: .* \(FAIL_BAD_CONDITIONAL_BRANCH\)}} # CHECK-NEXT: tiny.cc:11 # CHECK: Expected Protected: 0 (0.00%) diff --git a/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp b/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp index 245ce05a254e5e..b221e5a833273d 100644 --- a/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp +++ b/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp @@ -41,9 +41,87 @@ cl::opt PrintGraphs( "print-graphs", cl::desc("Print graphs around indirect CF instructions in DOT format."), cl::init(false)); +cl::opt PrintBlameContext( + "blame-context", + cl::desc("Print the blame context (if possible) for BAD instructions. This " + "specifies the number of lines of context to include, where zero " + "disables this feature."), + cl::init(0)); +cl::opt PrintBlameContextAll( + "blame-context-all", + cl::desc("Prints the blame context (if possible) for ALL instructions. " + "This specifies the number of lines of context for non-BAD " + "instructions (see --blame-context). If --blame-context is " + "unspecified, it prints this number of contextual lines for BAD " + "instructions as well."), + cl::init(0)); +cl::opt Summarize("summarize", cl::desc("Print the summary only."), + cl::init(false)); ExitOnError ExitOnErr; +void printBlameContext(const DILineInfo &LineInfo, unsigned Context) { + auto FileOrErr = MemoryBuffer::getFile(LineInfo.FileName); + if (!FileOrErr) { + errs() << "Could not open file: " << LineInfo.FileName << "\n"; + return; + } + + std::unique_ptr File = std::move(FileOrErr.get()); + SmallVector Lines; + File->getBuffer().split(Lines, '\n'); + + for (unsigned i = std::max(1l, (long)LineInfo.Line - Context); + i < + std::min(Lines.size() + 1, (unsigned long)LineInfo.Line + Context + 1); + ++i) { + if (i == LineInfo.Line) + outs() << ">"; + else + outs() << " "; + + outs() << i << ": " << Lines[i - 1] << "\n"; + } +} + +void printInstructionInformation(const FileAnalysis &Analysis, + const Instr &InstrMeta, + const GraphResult &Graph, + CFIProtectionStatus ProtectionStatus) { + outs() << "Instruction: " << format_hex(InstrMeta.VMAddress, 2) << " (" + << stringCFIProtectionStatus(ProtectionStatus) << "): "; + Analysis.printInstruction(InstrMeta, outs()); + outs() << " \n"; + + if (PrintGraphs) + Graph.printToDOT(Analysis, outs()); +} + +void printInstructionStatus(unsigned BlameLine, bool CFIProtected, + const DILineInfo &LineInfo) { + if (BlameLine) { + outs() << "Blacklist Match: " << BlacklistFilename << ":" << BlameLine + << "\n"; + if (CFIProtected) + outs() << "====> Unexpected Protected\n"; + else + outs() << "====> Expected Unprotected\n"; + + if (PrintBlameContextAll) + printBlameContext(LineInfo, PrintBlameContextAll); + } else { + if (CFIProtected) { + outs() << "====> Expected Protected\n"; + if (PrintBlameContextAll) + printBlameContext(LineInfo, PrintBlameContextAll); + } else { + outs() << "====> Unexpected Unprotected (BAD)\n"; + if (PrintBlameContext) + printBlameContext(LineInfo, PrintBlameContext); + } + } +} + void printIndirectCFInstructions(FileAnalysis &Analysis, const SpecialCaseList *SpecialCaseList) { uint64_t ExpectedProtected = 0; @@ -61,17 +139,10 @@ void printIndirectCFInstructions(FileAnalysis &Analysis, Analysis.validateCFIProtection(Graph); bool CFIProtected = (ProtectionStatus == CFIProtectionStatus::PROTECTED); - if (CFIProtected) - outs() << "P "; - else - outs() << "U "; - - outs() << format_hex(Address, 2) << " | "; - Analysis.printInstruction(InstrMeta, outs()); - outs() << " \n"; - - if (PrintGraphs) - Graph.printToDOT(Analysis, outs()); + if (!Summarize) { + outs() << "-----------------------------------------------------\n"; + printInstructionInformation(Analysis, InstrMeta, Graph, ProtectionStatus); + } if (IgnoreDWARFFlag) { if (CFIProtected) @@ -88,22 +159,28 @@ void printIndirectCFInstructions(FileAnalysis &Analysis, exit(EXIT_FAILURE); } - const auto &LineInfo = - InliningInfo->getFrame(InliningInfo->getNumberOfFrames() - 1); + const auto &LineInfo = InliningInfo->getFrame(0); // Print the inlining symbolisation of this instruction. - for (uint32_t i = 0; i < InliningInfo->getNumberOfFrames(); ++i) { - const auto &Line = InliningInfo->getFrame(i); - outs() << " " << format_hex(Address, 2) << " = " << Line.FileName << ":" - << Line.Line << ":" << Line.Column << " (" << Line.FunctionName - << ")\n"; + if (!Summarize) { + for (uint32_t i = 0; i < InliningInfo->getNumberOfFrames(); ++i) { + const auto &Line = InliningInfo->getFrame(i); + outs() << " " << format_hex(Address, 2) << " = " << Line.FileName + << ":" << Line.Line << ":" << Line.Column << " (" + << Line.FunctionName << ")\n"; + } } if (!SpecialCaseList) { - if (CFIProtected) + if (CFIProtected) { + if (PrintBlameContextAll && !Summarize) + printBlameContext(LineInfo, PrintBlameContextAll); ExpectedProtected++; - else + } else { + if (PrintBlameContext && !Summarize) + printBlameContext(LineInfo, PrintBlameContext); UnexpectedUnprotected++; + } continue; } @@ -118,25 +195,20 @@ void printIndirectCFInstructions(FileAnalysis &Analysis, } if (BlameLine) { - outs() << "Blacklist Match: " << BlacklistFilename << ":" << BlameLine - << "\n"; BlameCounter[BlameLine]++; - if (CFIProtected) { + if (CFIProtected) UnexpectedProtected++; - outs() << "====> Unexpected Protected\n"; - } else { + else ExpectedUnprotected++; - outs() << "====> Expected Unprotected\n"; - } } else { - if (CFIProtected) { + if (CFIProtected) ExpectedProtected++; - outs() << "====> Expected Protected\n"; - } else { + else UnexpectedUnprotected++; - outs() << "====> Unexpected Unprotected\n"; - } } + + if (!Summarize) + printInstructionStatus(BlameLine, CFIProtected, LineInfo); } uint64_t IndirectCFInstructions = ExpectedProtected + UnexpectedProtected + @@ -147,11 +219,12 @@ void printIndirectCFInstructions(FileAnalysis &Analysis, return; } - outs() << formatv("Expected Protected: {0} ({1:P})\n" - "Unexpected Protected: {2} ({3:P})\n" - "Expected Unprotected: {4} ({5:P})\n" - "Unexpected Unprotected (BAD): {6} ({7:P})\n", - ExpectedProtected, + outs() << formatv("\nTotal Indirect CF Instructions: {0}\n" + "Expected Protected: {1} ({2:P})\n" + "Unexpected Protected: {3} ({4:P})\n" + "Expected Unprotected: {5} ({6:P})\n" + "Unexpected Unprotected (BAD): {7} ({8:P})\n", + IndirectCFInstructions, ExpectedProtected, ((double)ExpectedProtected) / IndirectCFInstructions, UnexpectedProtected, ((double)UnexpectedProtected) / IndirectCFInstructions, @@ -163,7 +236,7 @@ void printIndirectCFInstructions(FileAnalysis &Analysis, if (!SpecialCaseList) return; - outs() << "Blacklist Results:\n"; + outs() << "\nBlacklist Results:\n"; for (const auto &KV : BlameCounter) { outs() << " " << BlacklistFilename << ":" << KV.first << " affects " << KV.second << " indirect CF instructions.\n"; @@ -183,6 +256,9 @@ int main(int argc, char **argv) { InitializeAllAsmParsers(); InitializeAllDisassemblers(); + if (PrintBlameContextAll && !PrintBlameContext) + PrintBlameContext.setValue(PrintBlameContextAll); + std::unique_ptr SpecialCaseList; if (BlacklistFilename != "-") { std::string Error;