blob: 245ce05a254e5e0ac4728edf69a0a90df1f2d042 [file] [log] [blame]
Vlad Tsyrklevich31b45312017-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 Tsyrklevich89c3c8c2017-10-11 20:35:01 +000020#include "lib/FileAnalysis.h"
Mitch Phillips3b9ea322017-11-10 21:00:22 +000021#include "lib/GraphBuilder.h"
Vlad Tsyrklevichb5488a22017-10-10 20:59:08 +000022
Vlad Tsyrklevich89c3c8c2017-10-11 20:35:01 +000023#include "llvm/BinaryFormat/ELF.h"
24#include "llvm/Support/CommandLine.h"
25#include "llvm/Support/Error.h"
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000026#include "llvm/Support/FormatVariadic.h"
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000027#include "llvm/Support/SpecialCaseList.h"
Vlad Tsyrklevich89c3c8c2017-10-11 20:35:01 +000028
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +000029#include <cstdlib>
30
31using namespace llvm;
32using namespace llvm::object;
Vlad Tsyrklevich89c3c8c2017-10-11 20:35:01 +000033using namespace llvm::cfi_verify;
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +000034
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +000035cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input file>"),
36 cl::Required);
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000037cl::opt<std::string> BlacklistFilename(cl::Positional,
38 cl::desc("[blacklist file]"),
39 cl::init("-"));
Mitch Phillips02993892017-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 Tsyrklevich31b45312017-09-20 20:38:14 +000044
Vlad Tsyrklevich89c3c8c2017-10-11 20:35:01 +000045ExitOnError ExitOnErr;
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +000046
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000047void printIndirectCFInstructions(FileAnalysis &Analysis,
48 const SpecialCaseList *SpecialCaseList) {
49 uint64_t ExpectedProtected = 0;
50 uint64_t UnexpectedProtected = 0;
51 uint64_t ExpectedUnprotected = 0;
52 uint64_t UnexpectedUnprotected = 0;
53
Mitch Phillipsd64af522017-11-09 00:18:31 +000054 std::map<unsigned, uint64_t> BlameCounter;
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000055
56 for (uint64_t Address : Analysis.getIndirectInstructions()) {
57 const auto &InstrMeta = Analysis.getInstructionOrDie(Address);
Mitch Phillips3b9ea322017-11-10 21:00:22 +000058 GraphResult Graph = GraphBuilder::buildFlowGraph(Analysis, Address);
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000059
Mitch Phillips3b9ea322017-11-10 21:00:22 +000060 CFIProtectionStatus ProtectionStatus =
61 Analysis.validateCFIProtection(Graph);
62 bool CFIProtected = (ProtectionStatus == CFIProtectionStatus::PROTECTED);
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000063
64 if (CFIProtected)
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000065 outs() << "P ";
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000066 else
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000067 outs() << "U ";
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000068
Mitch Phillips02993892017-11-14 22:43:13 +000069 outs() << format_hex(Address, 2) << " | ";
70 Analysis.printInstruction(InstrMeta, outs());
71 outs() << " \n";
72
73 if (PrintGraphs)
74 Graph.printToDOT(Analysis, outs());
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000075
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000076 if (IgnoreDWARFFlag) {
77 if (CFIProtected)
78 ExpectedProtected++;
79 else
80 UnexpectedUnprotected++;
81 continue;
82 }
83
Mitch Phillips3b9ea322017-11-10 21:00:22 +000084 auto InliningInfo = Analysis.symbolizeInlinedCode(Address);
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000085 if (!InliningInfo || InliningInfo->getNumberOfFrames() == 0) {
86 errs() << "Failed to symbolise " << format_hex(Address, 2)
87 << " with line tables from " << InputFilename << "\n";
88 exit(EXIT_FAILURE);
89 }
90
91 const auto &LineInfo =
92 InliningInfo->getFrame(InliningInfo->getNumberOfFrames() - 1);
93
94 // Print the inlining symbolisation of this instruction.
95 for (uint32_t i = 0; i < InliningInfo->getNumberOfFrames(); ++i) {
96 const auto &Line = InliningInfo->getFrame(i);
97 outs() << " " << format_hex(Address, 2) << " = " << Line.FileName << ":"
98 << Line.Line << ":" << Line.Column << " (" << Line.FunctionName
99 << ")\n";
100 }
101
102 if (!SpecialCaseList) {
103 if (CFIProtected)
104 ExpectedProtected++;
105 else
106 UnexpectedUnprotected++;
107 continue;
108 }
109
Mitch Phillipsd64af522017-11-09 00:18:31 +0000110 unsigned BlameLine = 0;
111 for (auto &K : {"cfi-icall", "cfi-vcall"}) {
112 if (!BlameLine)
113 BlameLine =
114 SpecialCaseList->inSectionBlame(K, "src", LineInfo.FileName);
115 if (!BlameLine)
116 BlameLine =
117 SpecialCaseList->inSectionBlame(K, "fun", LineInfo.FunctionName);
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000118 }
119
Mitch Phillipsd64af522017-11-09 00:18:31 +0000120 if (BlameLine) {
121 outs() << "Blacklist Match: " << BlacklistFilename << ":" << BlameLine
122 << "\n";
123 BlameCounter[BlameLine]++;
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000124 if (CFIProtected) {
125 UnexpectedProtected++;
126 outs() << "====> Unexpected Protected\n";
127 } else {
128 ExpectedUnprotected++;
129 outs() << "====> Expected Unprotected\n";
130 }
131 } else {
132 if (CFIProtected) {
133 ExpectedProtected++;
134 outs() << "====> Expected Protected\n";
135 } else {
136 UnexpectedUnprotected++;
137 outs() << "====> Unexpected Unprotected\n";
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000138 }
139 }
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +0000140 }
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000141
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000142 uint64_t IndirectCFInstructions = ExpectedProtected + UnexpectedProtected +
143 ExpectedUnprotected + UnexpectedUnprotected;
144
Mitch Phillips6fb35252017-11-06 19:14:09 +0000145 if (IndirectCFInstructions == 0) {
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000146 outs() << "No indirect CF instructions found.\n";
Mitch Phillips6fb35252017-11-06 19:14:09 +0000147 return;
148 }
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000149
150 outs() << formatv("Expected Protected: {0} ({1:P})\n"
151 "Unexpected Protected: {2} ({3:P})\n"
152 "Expected Unprotected: {4} ({5:P})\n"
153 "Unexpected Unprotected (BAD): {6} ({7:P})\n",
154 ExpectedProtected,
155 ((double)ExpectedProtected) / IndirectCFInstructions,
156 UnexpectedProtected,
157 ((double)UnexpectedProtected) / IndirectCFInstructions,
158 ExpectedUnprotected,
159 ((double)ExpectedUnprotected) / IndirectCFInstructions,
160 UnexpectedUnprotected,
161 ((double)UnexpectedUnprotected) / IndirectCFInstructions);
Mitch Phillipsd64af522017-11-09 00:18:31 +0000162
163 if (!SpecialCaseList)
164 return;
165
166 outs() << "Blacklist Results:\n";
167 for (const auto &KV : BlameCounter) {
168 outs() << " " << BlacklistFilename << ":" << KV.first << " affects "
169 << KV.second << " indirect CF instructions.\n";
170 }
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +0000171}
172
173int main(int argc, char **argv) {
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000174 cl::ParseCommandLineOptions(
175 argc, argv,
176 "Identifies whether Control Flow Integrity protects all indirect control "
177 "flow instructions in the provided object file, DSO or binary.\nNote: "
178 "Anything statically linked into the provided file *must* be compiled "
179 "with '-g'. This can be relaxed through the '--ignore-dwarf' flag.");
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +0000180
181 InitializeAllTargetInfos();
182 InitializeAllTargetMCs();
183 InitializeAllAsmParsers();
184 InitializeAllDisassemblers();
185
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000186 std::unique_ptr<SpecialCaseList> SpecialCaseList;
187 if (BlacklistFilename != "-") {
188 std::string Error;
189 SpecialCaseList = SpecialCaseList::create({BlacklistFilename}, Error);
190 if (!SpecialCaseList) {
191 errs() << "Failed to get blacklist: " << Error << "\n";
192 exit(EXIT_FAILURE);
193 }
194 }
195
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000196 FileAnalysis Analysis = ExitOnErr(FileAnalysis::Create(InputFilename));
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000197 printIndirectCFInstructions(Analysis, SpecialCaseList.get());
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +0000198
199 return EXIT_SUCCESS;
200}