[llvm-readobj] - Refine the LLVM-style output to be consistent.
[llvm-complete.git] / tools / llvm-cfi-verify / llvm-cfi-verify.cpp
blobc54e5383248247302173ff8f2e664acf44954280
1 //===-- llvm-cfi-verify.cpp - CFI Verification tool for LLVM --------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This tool verifies Control Flow Integrity (CFI) instrumentation by static
10 // binary anaylsis. See the design document in /docs/CFIVerify.rst for more
11 // information.
13 // This tool is currently incomplete. It currently only does disassembly for
14 // object files, and searches through the code for indirect control flow
15 // instructions, printing them once found.
17 //===----------------------------------------------------------------------===//
19 #include "lib/FileAnalysis.h"
20 #include "lib/GraphBuilder.h"
22 #include "llvm/BinaryFormat/ELF.h"
23 #include "llvm/Support/CommandLine.h"
24 #include "llvm/Support/Error.h"
25 #include "llvm/Support/FormatVariadic.h"
26 #include "llvm/Support/SpecialCaseList.h"
28 #include <cstdlib>
30 using namespace llvm;
31 using namespace llvm::object;
32 using namespace llvm::cfi_verify;
34 cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input file>"),
35 cl::Required);
36 cl::opt<std::string> BlacklistFilename(cl::Positional,
37 cl::desc("[blacklist file]"),
38 cl::init("-"));
39 cl::opt<bool> PrintGraphs(
40 "print-graphs",
41 cl::desc("Print graphs around indirect CF instructions in DOT format."),
42 cl::init(false));
43 cl::opt<unsigned> PrintBlameContext(
44 "blame-context",
45 cl::desc("Print the blame context (if possible) for BAD instructions. This "
46 "specifies the number of lines of context to include, where zero "
47 "disables this feature."),
48 cl::init(0));
49 cl::opt<unsigned> PrintBlameContextAll(
50 "blame-context-all",
51 cl::desc("Prints the blame context (if possible) for ALL instructions. "
52 "This specifies the number of lines of context for non-BAD "
53 "instructions (see --blame-context). If --blame-context is "
54 "unspecified, it prints this number of contextual lines for BAD "
55 "instructions as well."),
56 cl::init(0));
57 cl::opt<bool> Summarize("summarize", cl::desc("Print the summary only."),
58 cl::init(false));
60 ExitOnError ExitOnErr;
62 void printBlameContext(const DILineInfo &LineInfo, unsigned Context) {
63 auto FileOrErr = MemoryBuffer::getFile(LineInfo.FileName);
64 if (!FileOrErr) {
65 errs() << "Could not open file: " << LineInfo.FileName << "\n";
66 return;
69 std::unique_ptr<MemoryBuffer> File = std::move(FileOrErr.get());
70 SmallVector<StringRef, 100> Lines;
71 File->getBuffer().split(Lines, '\n');
73 for (unsigned i = std::max<size_t>(1, LineInfo.Line - Context);
74 i <
75 std::min<size_t>(Lines.size() + 1, LineInfo.Line + Context + 1);
76 ++i) {
77 if (i == LineInfo.Line)
78 outs() << ">";
79 else
80 outs() << " ";
82 outs() << i << ": " << Lines[i - 1] << "\n";
86 void printInstructionInformation(const FileAnalysis &Analysis,
87 const Instr &InstrMeta,
88 const GraphResult &Graph,
89 CFIProtectionStatus ProtectionStatus) {
90 outs() << "Instruction: " << format_hex(InstrMeta.VMAddress, 2) << " ("
91 << stringCFIProtectionStatus(ProtectionStatus) << "): ";
92 Analysis.printInstruction(InstrMeta, outs());
93 outs() << " \n";
95 if (PrintGraphs)
96 Graph.printToDOT(Analysis, outs());
99 void printInstructionStatus(unsigned BlameLine, bool CFIProtected,
100 const DILineInfo &LineInfo) {
101 if (BlameLine) {
102 outs() << "Blacklist Match: " << BlacklistFilename << ":" << BlameLine
103 << "\n";
104 if (CFIProtected)
105 outs() << "====> Unexpected Protected\n";
106 else
107 outs() << "====> Expected Unprotected\n";
109 if (PrintBlameContextAll)
110 printBlameContext(LineInfo, PrintBlameContextAll);
111 } else {
112 if (CFIProtected) {
113 outs() << "====> Expected Protected\n";
114 if (PrintBlameContextAll)
115 printBlameContext(LineInfo, PrintBlameContextAll);
116 } else {
117 outs() << "====> Unexpected Unprotected (BAD)\n";
118 if (PrintBlameContext)
119 printBlameContext(LineInfo, PrintBlameContext);
124 void printIndirectCFInstructions(FileAnalysis &Analysis,
125 const SpecialCaseList *SpecialCaseList) {
126 uint64_t ExpectedProtected = 0;
127 uint64_t UnexpectedProtected = 0;
128 uint64_t ExpectedUnprotected = 0;
129 uint64_t UnexpectedUnprotected = 0;
131 std::map<unsigned, uint64_t> BlameCounter;
133 for (object::SectionedAddress Address : Analysis.getIndirectInstructions()) {
134 const auto &InstrMeta = Analysis.getInstructionOrDie(Address.Address);
135 GraphResult Graph = GraphBuilder::buildFlowGraph(Analysis, Address);
137 CFIProtectionStatus ProtectionStatus =
138 Analysis.validateCFIProtection(Graph);
139 bool CFIProtected = (ProtectionStatus == CFIProtectionStatus::PROTECTED);
141 if (!Summarize) {
142 outs() << "-----------------------------------------------------\n";
143 printInstructionInformation(Analysis, InstrMeta, Graph, ProtectionStatus);
146 if (IgnoreDWARFFlag) {
147 if (CFIProtected)
148 ExpectedProtected++;
149 else
150 UnexpectedUnprotected++;
151 continue;
154 auto InliningInfo = Analysis.symbolizeInlinedCode(Address);
155 if (!InliningInfo || InliningInfo->getNumberOfFrames() == 0) {
156 errs() << "Failed to symbolise " << format_hex(Address.Address, 2)
157 << " with line tables from " << InputFilename << "\n";
158 exit(EXIT_FAILURE);
161 const auto &LineInfo = InliningInfo->getFrame(0);
163 // Print the inlining symbolisation of this instruction.
164 if (!Summarize) {
165 for (uint32_t i = 0; i < InliningInfo->getNumberOfFrames(); ++i) {
166 const auto &Line = InliningInfo->getFrame(i);
167 outs() << " " << format_hex(Address.Address, 2) << " = "
168 << Line.FileName << ":" << Line.Line << ":" << Line.Column
169 << " (" << Line.FunctionName << ")\n";
173 if (!SpecialCaseList) {
174 if (CFIProtected) {
175 if (PrintBlameContextAll && !Summarize)
176 printBlameContext(LineInfo, PrintBlameContextAll);
177 ExpectedProtected++;
178 } else {
179 if (PrintBlameContext && !Summarize)
180 printBlameContext(LineInfo, PrintBlameContext);
181 UnexpectedUnprotected++;
183 continue;
186 unsigned BlameLine = 0;
187 for (auto &K : {"cfi-icall", "cfi-vcall"}) {
188 if (!BlameLine)
189 BlameLine =
190 SpecialCaseList->inSectionBlame(K, "src", LineInfo.FileName);
191 if (!BlameLine)
192 BlameLine =
193 SpecialCaseList->inSectionBlame(K, "fun", LineInfo.FunctionName);
196 if (BlameLine) {
197 BlameCounter[BlameLine]++;
198 if (CFIProtected)
199 UnexpectedProtected++;
200 else
201 ExpectedUnprotected++;
202 } else {
203 if (CFIProtected)
204 ExpectedProtected++;
205 else
206 UnexpectedUnprotected++;
209 if (!Summarize)
210 printInstructionStatus(BlameLine, CFIProtected, LineInfo);
213 uint64_t IndirectCFInstructions = ExpectedProtected + UnexpectedProtected +
214 ExpectedUnprotected + UnexpectedUnprotected;
216 if (IndirectCFInstructions == 0) {
217 outs() << "No indirect CF instructions found.\n";
218 return;
221 outs() << formatv("\nTotal Indirect CF Instructions: {0}\n"
222 "Expected Protected: {1} ({2:P})\n"
223 "Unexpected Protected: {3} ({4:P})\n"
224 "Expected Unprotected: {5} ({6:P})\n"
225 "Unexpected Unprotected (BAD): {7} ({8:P})\n",
226 IndirectCFInstructions, ExpectedProtected,
227 ((double)ExpectedProtected) / IndirectCFInstructions,
228 UnexpectedProtected,
229 ((double)UnexpectedProtected) / IndirectCFInstructions,
230 ExpectedUnprotected,
231 ((double)ExpectedUnprotected) / IndirectCFInstructions,
232 UnexpectedUnprotected,
233 ((double)UnexpectedUnprotected) / IndirectCFInstructions);
235 if (!SpecialCaseList)
236 return;
238 outs() << "\nBlacklist Results:\n";
239 for (const auto &KV : BlameCounter) {
240 outs() << " " << BlacklistFilename << ":" << KV.first << " affects "
241 << KV.second << " indirect CF instructions.\n";
245 int main(int argc, char **argv) {
246 cl::ParseCommandLineOptions(
247 argc, argv,
248 "Identifies whether Control Flow Integrity protects all indirect control "
249 "flow instructions in the provided object file, DSO or binary.\nNote: "
250 "Anything statically linked into the provided file *must* be compiled "
251 "with '-g'. This can be relaxed through the '--ignore-dwarf' flag.");
253 InitializeAllTargetInfos();
254 InitializeAllTargetMCs();
255 InitializeAllAsmParsers();
256 InitializeAllDisassemblers();
258 if (PrintBlameContextAll && !PrintBlameContext)
259 PrintBlameContext.setValue(PrintBlameContextAll);
261 std::unique_ptr<SpecialCaseList> SpecialCaseList;
262 if (BlacklistFilename != "-") {
263 std::string Error;
264 SpecialCaseList = SpecialCaseList::create({BlacklistFilename}, Error);
265 if (!SpecialCaseList) {
266 errs() << "Failed to get blacklist: " << Error << "\n";
267 exit(EXIT_FAILURE);
271 FileAnalysis Analysis = ExitOnErr(FileAnalysis::Create(InputFilename));
272 printIndirectCFInstructions(Analysis, SpecialCaseList.get());
274 return EXIT_SUCCESS;