blob: 2fde74bd24c9beabb4073a771e85c2578d605982 [file] [log] [blame]
Vlad Tsyrklevichbbb32802017-09-20 20:38:14 +00001//===-- llvm-cfi-verify.cpp - CFI Verification tool for LLVM --------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This tool verifies Control Flow Integrity (CFI) instrumentation by static
11// binary anaylsis. See the design document in /docs/CFIVerify.rst for more
12// information.
13//
14// This tool is currently incomplete. It currently only does disassembly for
15// object files, and searches through the code for indirect control flow
16// instructions, printing them once found.
17//
18//===----------------------------------------------------------------------===//
19
Vlad Tsyrklevich9c28c0a2017-10-11 20:35:01 +000020#include "lib/FileAnalysis.h"
Mitch Phillips7c7bb612017-11-10 21:00:22 +000021#include "lib/GraphBuilder.h"
Vlad Tsyrklevichf4ea1252017-10-10 20:59:08 +000022
Vlad Tsyrklevich9c28c0a2017-10-11 20:35:01 +000023#include "llvm/BinaryFormat/ELF.h"
24#include "llvm/Support/CommandLine.h"
25#include "llvm/Support/Error.h"
Mitch Phillipsb2fffde2017-10-31 23:20:05 +000026#include "llvm/Support/FormatVariadic.h"
Mitch Phillips79eed692017-11-03 20:54:26 +000027#include "llvm/Support/SpecialCaseList.h"
Vlad Tsyrklevich9c28c0a2017-10-11 20:35:01 +000028
Vlad Tsyrklevichbbb32802017-09-20 20:38:14 +000029#include <cstdlib>
30
31using namespace llvm;
32using namespace llvm::object;
Vlad Tsyrklevich9c28c0a2017-10-11 20:35:01 +000033using namespace llvm::cfi_verify;
Vlad Tsyrklevichbbb32802017-09-20 20:38:14 +000034
Vlad Tsyrklevichbbb32802017-09-20 20:38:14 +000035cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input file>"),
36 cl::Required);
Mitch Phillips79eed692017-11-03 20:54:26 +000037cl::opt<std::string> BlacklistFilename(cl::Positional,
38 cl::desc("[blacklist file]"),
39 cl::init("-"));
Mitch Phillips9300d722017-11-14 22:43:13 +000040cl::opt<bool> PrintGraphs(
41 "print-graphs",
42 cl::desc("Print graphs around indirect CF instructions in DOT format."),
43 cl::init(false));
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +000044cl::opt<unsigned> PrintBlameContext(
45 "blame-context",
46 cl::desc("Print the blame context (if possible) for BAD instructions. This "
47 "specifies the number of lines of context to include, where zero "
48 "disables this feature."),
49 cl::init(0));
50cl::opt<unsigned> PrintBlameContextAll(
51 "blame-context-all",
52 cl::desc("Prints the blame context (if possible) for ALL instructions. "
53 "This specifies the number of lines of context for non-BAD "
54 "instructions (see --blame-context). If --blame-context is "
55 "unspecified, it prints this number of contextual lines for BAD "
56 "instructions as well."),
57 cl::init(0));
58cl::opt<bool> Summarize("summarize", cl::desc("Print the summary only."),
59 cl::init(false));
Vlad Tsyrklevichbbb32802017-09-20 20:38:14 +000060
Vlad Tsyrklevich9c28c0a2017-10-11 20:35:01 +000061ExitOnError ExitOnErr;
Vlad Tsyrklevichbbb32802017-09-20 20:38:14 +000062
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +000063void printBlameContext(const DILineInfo &LineInfo, unsigned Context) {
64 auto FileOrErr = MemoryBuffer::getFile(LineInfo.FileName);
65 if (!FileOrErr) {
66 errs() << "Could not open file: " << LineInfo.FileName << "\n";
67 return;
68 }
69
70 std::unique_ptr<MemoryBuffer> File = std::move(FileOrErr.get());
71 SmallVector<StringRef, 100> Lines;
72 File->getBuffer().split(Lines, '\n');
73
Vlad Tsyrklevich23475152018-02-02 00:07:14 +000074 for (unsigned i = std::max<size_t>(1, LineInfo.Line - Context);
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +000075 i <
Vlad Tsyrklevich23475152018-02-02 00:07:14 +000076 std::min<size_t>(Lines.size() + 1, LineInfo.Line + Context + 1);
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +000077 ++i) {
78 if (i == LineInfo.Line)
79 outs() << ">";
80 else
81 outs() << " ";
82
83 outs() << i << ": " << Lines[i - 1] << "\n";
84 }
85}
86
87void printInstructionInformation(const FileAnalysis &Analysis,
88 const Instr &InstrMeta,
89 const GraphResult &Graph,
90 CFIProtectionStatus ProtectionStatus) {
91 outs() << "Instruction: " << format_hex(InstrMeta.VMAddress, 2) << " ("
92 << stringCFIProtectionStatus(ProtectionStatus) << "): ";
93 Analysis.printInstruction(InstrMeta, outs());
94 outs() << " \n";
95
96 if (PrintGraphs)
97 Graph.printToDOT(Analysis, outs());
98}
99
100void printInstructionStatus(unsigned BlameLine, bool CFIProtected,
101 const DILineInfo &LineInfo) {
102 if (BlameLine) {
103 outs() << "Blacklist Match: " << BlacklistFilename << ":" << BlameLine
104 << "\n";
105 if (CFIProtected)
106 outs() << "====> Unexpected Protected\n";
107 else
108 outs() << "====> Expected Unprotected\n";
109
110 if (PrintBlameContextAll)
111 printBlameContext(LineInfo, PrintBlameContextAll);
112 } else {
113 if (CFIProtected) {
114 outs() << "====> Expected Protected\n";
115 if (PrintBlameContextAll)
116 printBlameContext(LineInfo, PrintBlameContextAll);
117 } else {
118 outs() << "====> Unexpected Unprotected (BAD)\n";
119 if (PrintBlameContext)
120 printBlameContext(LineInfo, PrintBlameContext);
121 }
122 }
123}
124
Mitch Phillips79eed692017-11-03 20:54:26 +0000125void printIndirectCFInstructions(FileAnalysis &Analysis,
126 const SpecialCaseList *SpecialCaseList) {
127 uint64_t ExpectedProtected = 0;
128 uint64_t UnexpectedProtected = 0;
129 uint64_t ExpectedUnprotected = 0;
130 uint64_t UnexpectedUnprotected = 0;
131
Mitch Phillips18c0af12017-11-09 00:18:31 +0000132 std::map<unsigned, uint64_t> BlameCounter;
Mitch Phillipsb2fffde2017-10-31 23:20:05 +0000133
134 for (uint64_t Address : Analysis.getIndirectInstructions()) {
135 const auto &InstrMeta = Analysis.getInstructionOrDie(Address);
Mitch Phillips7c7bb612017-11-10 21:00:22 +0000136 GraphResult Graph = GraphBuilder::buildFlowGraph(Analysis, Address);
Mitch Phillipsb2fffde2017-10-31 23:20:05 +0000137
Mitch Phillips7c7bb612017-11-10 21:00:22 +0000138 CFIProtectionStatus ProtectionStatus =
139 Analysis.validateCFIProtection(Graph);
140 bool CFIProtected = (ProtectionStatus == CFIProtectionStatus::PROTECTED);
Mitch Phillips79eed692017-11-03 20:54:26 +0000141
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000142 if (!Summarize) {
143 outs() << "-----------------------------------------------------\n";
144 printInstructionInformation(Analysis, InstrMeta, Graph, ProtectionStatus);
145 }
Mitch Phillipsb2fffde2017-10-31 23:20:05 +0000146
Mitch Phillips79eed692017-11-03 20:54:26 +0000147 if (IgnoreDWARFFlag) {
148 if (CFIProtected)
149 ExpectedProtected++;
150 else
151 UnexpectedUnprotected++;
152 continue;
153 }
154
Mitch Phillips7c7bb612017-11-10 21:00:22 +0000155 auto InliningInfo = Analysis.symbolizeInlinedCode(Address);
Mitch Phillips79eed692017-11-03 20:54:26 +0000156 if (!InliningInfo || InliningInfo->getNumberOfFrames() == 0) {
157 errs() << "Failed to symbolise " << format_hex(Address, 2)
158 << " with line tables from " << InputFilename << "\n";
159 exit(EXIT_FAILURE);
160 }
161
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000162 const auto &LineInfo = InliningInfo->getFrame(0);
Mitch Phillips79eed692017-11-03 20:54:26 +0000163
164 // Print the inlining symbolisation of this instruction.
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000165 if (!Summarize) {
166 for (uint32_t i = 0; i < InliningInfo->getNumberOfFrames(); ++i) {
167 const auto &Line = InliningInfo->getFrame(i);
168 outs() << " " << format_hex(Address, 2) << " = " << Line.FileName
169 << ":" << Line.Line << ":" << Line.Column << " ("
170 << Line.FunctionName << ")\n";
171 }
Mitch Phillips79eed692017-11-03 20:54:26 +0000172 }
173
174 if (!SpecialCaseList) {
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000175 if (CFIProtected) {
176 if (PrintBlameContextAll && !Summarize)
177 printBlameContext(LineInfo, PrintBlameContextAll);
Mitch Phillips79eed692017-11-03 20:54:26 +0000178 ExpectedProtected++;
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000179 } else {
180 if (PrintBlameContext && !Summarize)
181 printBlameContext(LineInfo, PrintBlameContext);
Mitch Phillips79eed692017-11-03 20:54:26 +0000182 UnexpectedUnprotected++;
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000183 }
Mitch Phillips79eed692017-11-03 20:54:26 +0000184 continue;
185 }
186
Mitch Phillips18c0af12017-11-09 00:18:31 +0000187 unsigned BlameLine = 0;
188 for (auto &K : {"cfi-icall", "cfi-vcall"}) {
189 if (!BlameLine)
190 BlameLine =
191 SpecialCaseList->inSectionBlame(K, "src", LineInfo.FileName);
192 if (!BlameLine)
193 BlameLine =
194 SpecialCaseList->inSectionBlame(K, "fun", LineInfo.FunctionName);
Mitch Phillips79eed692017-11-03 20:54:26 +0000195 }
196
Mitch Phillips18c0af12017-11-09 00:18:31 +0000197 if (BlameLine) {
Mitch Phillips18c0af12017-11-09 00:18:31 +0000198 BlameCounter[BlameLine]++;
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000199 if (CFIProtected)
Mitch Phillips79eed692017-11-03 20:54:26 +0000200 UnexpectedProtected++;
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000201 else
Mitch Phillips79eed692017-11-03 20:54:26 +0000202 ExpectedUnprotected++;
Mitch Phillips79eed692017-11-03 20:54:26 +0000203 } else {
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000204 if (CFIProtected)
Mitch Phillips79eed692017-11-03 20:54:26 +0000205 ExpectedProtected++;
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000206 else
Mitch Phillips79eed692017-11-03 20:54:26 +0000207 UnexpectedUnprotected++;
Mitch Phillipsb2fffde2017-10-31 23:20:05 +0000208 }
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000209
210 if (!Summarize)
211 printInstructionStatus(BlameLine, CFIProtected, LineInfo);
Vlad Tsyrklevichbbb32802017-09-20 20:38:14 +0000212 }
Mitch Phillipsb2fffde2017-10-31 23:20:05 +0000213
Mitch Phillips79eed692017-11-03 20:54:26 +0000214 uint64_t IndirectCFInstructions = ExpectedProtected + UnexpectedProtected +
215 ExpectedUnprotected + UnexpectedUnprotected;
216
Mitch Phillips0ec29cb2017-11-06 19:14:09 +0000217 if (IndirectCFInstructions == 0) {
Mitch Phillipsb2fffde2017-10-31 23:20:05 +0000218 outs() << "No indirect CF instructions found.\n";
Mitch Phillips0ec29cb2017-11-06 19:14:09 +0000219 return;
220 }
Mitch Phillips79eed692017-11-03 20:54:26 +0000221
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000222 outs() << formatv("\nTotal Indirect CF Instructions: {0}\n"
223 "Expected Protected: {1} ({2:P})\n"
224 "Unexpected Protected: {3} ({4:P})\n"
225 "Expected Unprotected: {5} ({6:P})\n"
226 "Unexpected Unprotected (BAD): {7} ({8:P})\n",
227 IndirectCFInstructions, ExpectedProtected,
Mitch Phillips79eed692017-11-03 20:54:26 +0000228 ((double)ExpectedProtected) / IndirectCFInstructions,
229 UnexpectedProtected,
230 ((double)UnexpectedProtected) / IndirectCFInstructions,
231 ExpectedUnprotected,
232 ((double)ExpectedUnprotected) / IndirectCFInstructions,
233 UnexpectedUnprotected,
234 ((double)UnexpectedUnprotected) / IndirectCFInstructions);
Mitch Phillips18c0af12017-11-09 00:18:31 +0000235
236 if (!SpecialCaseList)
237 return;
238
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000239 outs() << "\nBlacklist Results:\n";
Mitch Phillips18c0af12017-11-09 00:18:31 +0000240 for (const auto &KV : BlameCounter) {
241 outs() << " " << BlacklistFilename << ":" << KV.first << " affects "
242 << KV.second << " indirect CF instructions.\n";
243 }
Vlad Tsyrklevichbbb32802017-09-20 20:38:14 +0000244}
245
246int main(int argc, char **argv) {
Mitch Phillipsb2fffde2017-10-31 23:20:05 +0000247 cl::ParseCommandLineOptions(
248 argc, argv,
249 "Identifies whether Control Flow Integrity protects all indirect control "
250 "flow instructions in the provided object file, DSO or binary.\nNote: "
251 "Anything statically linked into the provided file *must* be compiled "
252 "with '-g'. This can be relaxed through the '--ignore-dwarf' flag.");
Vlad Tsyrklevichbbb32802017-09-20 20:38:14 +0000253
254 InitializeAllTargetInfos();
255 InitializeAllTargetMCs();
256 InitializeAllAsmParsers();
257 InitializeAllDisassemblers();
258
Vlad Tsyrklevich4de242b2018-02-01 23:45:18 +0000259 if (PrintBlameContextAll && !PrintBlameContext)
260 PrintBlameContext.setValue(PrintBlameContextAll);
261
Mitch Phillips79eed692017-11-03 20:54:26 +0000262 std::unique_ptr<SpecialCaseList> SpecialCaseList;
263 if (BlacklistFilename != "-") {
264 std::string Error;
265 SpecialCaseList = SpecialCaseList::create({BlacklistFilename}, Error);
266 if (!SpecialCaseList) {
267 errs() << "Failed to get blacklist: " << Error << "\n";
268 exit(EXIT_FAILURE);
269 }
270 }
271
Mitch Phillipsb2fffde2017-10-31 23:20:05 +0000272 FileAnalysis Analysis = ExitOnErr(FileAnalysis::Create(InputFilename));
Mitch Phillips79eed692017-11-03 20:54:26 +0000273 printIndirectCFInstructions(Analysis, SpecialCaseList.get());
Vlad Tsyrklevichbbb32802017-09-20 20:38:14 +0000274
275 return EXIT_SUCCESS;
276}