blob: 8ae905e26365f9f4d9fb842501de566d5eb12c35 [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("-"));
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +000040
Vlad Tsyrklevich89c3c8c2017-10-11 20:35:01 +000041ExitOnError ExitOnErr;
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +000042
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000043void printIndirectCFInstructions(FileAnalysis &Analysis,
44 const SpecialCaseList *SpecialCaseList) {
45 uint64_t ExpectedProtected = 0;
46 uint64_t UnexpectedProtected = 0;
47 uint64_t ExpectedUnprotected = 0;
48 uint64_t UnexpectedUnprotected = 0;
49
Mitch Phillipsd64af522017-11-09 00:18:31 +000050 std::map<unsigned, uint64_t> BlameCounter;
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000051
52 for (uint64_t Address : Analysis.getIndirectInstructions()) {
53 const auto &InstrMeta = Analysis.getInstructionOrDie(Address);
Mitch Phillips3b9ea322017-11-10 21:00:22 +000054 GraphResult Graph = GraphBuilder::buildFlowGraph(Analysis, Address);
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000055
Mitch Phillips3b9ea322017-11-10 21:00:22 +000056 CFIProtectionStatus ProtectionStatus =
57 Analysis.validateCFIProtection(Graph);
58 bool CFIProtected = (ProtectionStatus == CFIProtectionStatus::PROTECTED);
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000059
60 if (CFIProtected)
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000061 outs() << "P ";
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000062 else
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000063 outs() << "U ";
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000064
65 outs() << format_hex(Address, 2) << " | "
66 << Analysis.getMCInstrInfo()->getName(
Vlad Tsyrklevich89c3c8c2017-10-11 20:35:01 +000067 InstrMeta.Instruction.getOpcode())
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000068 << " \n";
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000069
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000070 if (IgnoreDWARFFlag) {
71 if (CFIProtected)
72 ExpectedProtected++;
73 else
74 UnexpectedUnprotected++;
75 continue;
76 }
77
Mitch Phillips3b9ea322017-11-10 21:00:22 +000078 auto InliningInfo = Analysis.symbolizeInlinedCode(Address);
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000079 if (!InliningInfo || InliningInfo->getNumberOfFrames() == 0) {
80 errs() << "Failed to symbolise " << format_hex(Address, 2)
81 << " with line tables from " << InputFilename << "\n";
82 exit(EXIT_FAILURE);
83 }
84
85 const auto &LineInfo =
86 InliningInfo->getFrame(InliningInfo->getNumberOfFrames() - 1);
87
88 // Print the inlining symbolisation of this instruction.
89 for (uint32_t i = 0; i < InliningInfo->getNumberOfFrames(); ++i) {
90 const auto &Line = InliningInfo->getFrame(i);
91 outs() << " " << format_hex(Address, 2) << " = " << Line.FileName << ":"
92 << Line.Line << ":" << Line.Column << " (" << Line.FunctionName
93 << ")\n";
94 }
95
96 if (!SpecialCaseList) {
97 if (CFIProtected)
98 ExpectedProtected++;
99 else
100 UnexpectedUnprotected++;
101 continue;
102 }
103
Mitch Phillipsd64af522017-11-09 00:18:31 +0000104 unsigned BlameLine = 0;
105 for (auto &K : {"cfi-icall", "cfi-vcall"}) {
106 if (!BlameLine)
107 BlameLine =
108 SpecialCaseList->inSectionBlame(K, "src", LineInfo.FileName);
109 if (!BlameLine)
110 BlameLine =
111 SpecialCaseList->inSectionBlame(K, "fun", LineInfo.FunctionName);
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000112 }
113
Mitch Phillipsd64af522017-11-09 00:18:31 +0000114 if (BlameLine) {
115 outs() << "Blacklist Match: " << BlacklistFilename << ":" << BlameLine
116 << "\n";
117 BlameCounter[BlameLine]++;
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000118 if (CFIProtected) {
119 UnexpectedProtected++;
120 outs() << "====> Unexpected Protected\n";
121 } else {
122 ExpectedUnprotected++;
123 outs() << "====> Expected Unprotected\n";
124 }
125 } else {
126 if (CFIProtected) {
127 ExpectedProtected++;
128 outs() << "====> Expected Protected\n";
129 } else {
130 UnexpectedUnprotected++;
131 outs() << "====> Unexpected Unprotected\n";
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000132 }
133 }
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +0000134 }
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000135
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000136 uint64_t IndirectCFInstructions = ExpectedProtected + UnexpectedProtected +
137 ExpectedUnprotected + UnexpectedUnprotected;
138
Mitch Phillips6fb35252017-11-06 19:14:09 +0000139 if (IndirectCFInstructions == 0) {
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000140 outs() << "No indirect CF instructions found.\n";
Mitch Phillips6fb35252017-11-06 19:14:09 +0000141 return;
142 }
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000143
144 outs() << formatv("Expected Protected: {0} ({1:P})\n"
145 "Unexpected Protected: {2} ({3:P})\n"
146 "Expected Unprotected: {4} ({5:P})\n"
147 "Unexpected Unprotected (BAD): {6} ({7:P})\n",
148 ExpectedProtected,
149 ((double)ExpectedProtected) / IndirectCFInstructions,
150 UnexpectedProtected,
151 ((double)UnexpectedProtected) / IndirectCFInstructions,
152 ExpectedUnprotected,
153 ((double)ExpectedUnprotected) / IndirectCFInstructions,
154 UnexpectedUnprotected,
155 ((double)UnexpectedUnprotected) / IndirectCFInstructions);
Mitch Phillipsd64af522017-11-09 00:18:31 +0000156
157 if (!SpecialCaseList)
158 return;
159
160 outs() << "Blacklist Results:\n";
161 for (const auto &KV : BlameCounter) {
162 outs() << " " << BlacklistFilename << ":" << KV.first << " affects "
163 << KV.second << " indirect CF instructions.\n";
164 }
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +0000165}
166
167int main(int argc, char **argv) {
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000168 cl::ParseCommandLineOptions(
169 argc, argv,
170 "Identifies whether Control Flow Integrity protects all indirect control "
171 "flow instructions in the provided object file, DSO or binary.\nNote: "
172 "Anything statically linked into the provided file *must* be compiled "
173 "with '-g'. This can be relaxed through the '--ignore-dwarf' flag.");
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +0000174
175 InitializeAllTargetInfos();
176 InitializeAllTargetMCs();
177 InitializeAllAsmParsers();
178 InitializeAllDisassemblers();
179
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000180 std::unique_ptr<SpecialCaseList> SpecialCaseList;
181 if (BlacklistFilename != "-") {
182 std::string Error;
183 SpecialCaseList = SpecialCaseList::create({BlacklistFilename}, Error);
184 if (!SpecialCaseList) {
185 errs() << "Failed to get blacklist: " << Error << "\n";
186 exit(EXIT_FAILURE);
187 }
188 }
189
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000190 FileAnalysis Analysis = ExitOnErr(FileAnalysis::Create(InputFilename));
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000191 printIndirectCFInstructions(Analysis, SpecialCaseList.get());
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +0000192
193 return EXIT_SUCCESS;
194}