blob: a3c202f53bbc002254137c22e48f8838d47f347b [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"
Vlad Tsyrklevichb5488a22017-10-10 20:59:08 +000021
Vlad Tsyrklevich89c3c8c2017-10-11 20:35:01 +000022#include "llvm/BinaryFormat/ELF.h"
23#include "llvm/Support/CommandLine.h"
24#include "llvm/Support/Error.h"
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000025#include "llvm/Support/FormatVariadic.h"
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000026#include "llvm/Support/SpecialCaseList.h"
Vlad Tsyrklevich89c3c8c2017-10-11 20:35:01 +000027
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +000028#include <cstdlib>
29
30using namespace llvm;
31using namespace llvm::object;
Vlad Tsyrklevich89c3c8c2017-10-11 20:35:01 +000032using namespace llvm::cfi_verify;
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +000033
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +000034cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input file>"),
35 cl::Required);
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000036cl::opt<std::string> BlacklistFilename(cl::Positional,
37 cl::desc("[blacklist file]"),
38 cl::init("-"));
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +000039
Vlad Tsyrklevich89c3c8c2017-10-11 20:35:01 +000040ExitOnError ExitOnErr;
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +000041
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000042void printIndirectCFInstructions(FileAnalysis &Analysis,
43 const SpecialCaseList *SpecialCaseList) {
44 uint64_t ExpectedProtected = 0;
45 uint64_t UnexpectedProtected = 0;
46 uint64_t ExpectedUnprotected = 0;
47 uint64_t UnexpectedUnprotected = 0;
48
49 symbolize::LLVMSymbolizer &Symbolizer = Analysis.getSymbolizer();
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000050
51 for (uint64_t Address : Analysis.getIndirectInstructions()) {
52 const auto &InstrMeta = Analysis.getInstructionOrDie(Address);
53
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000054 bool CFIProtected = Analysis.isIndirectInstructionCFIProtected(Address);
55
56 if (CFIProtected)
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000057 outs() << "P ";
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000058 else
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000059 outs() << "U ";
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000060
61 outs() << format_hex(Address, 2) << " | "
62 << Analysis.getMCInstrInfo()->getName(
Vlad Tsyrklevich89c3c8c2017-10-11 20:35:01 +000063 InstrMeta.Instruction.getOpcode())
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000064 << " \n";
Mitch Phillips7db6f7a2017-10-31 23:20:05 +000065
Mitch Phillipsc15bdf52017-11-03 20:54:26 +000066 if (IgnoreDWARFFlag) {
67 if (CFIProtected)
68 ExpectedProtected++;
69 else
70 UnexpectedUnprotected++;
71 continue;
72 }
73
74 auto InliningInfo = Symbolizer.symbolizeInlinedCode(InputFilename, Address);
75 if (!InliningInfo || InliningInfo->getNumberOfFrames() == 0) {
76 errs() << "Failed to symbolise " << format_hex(Address, 2)
77 << " with line tables from " << InputFilename << "\n";
78 exit(EXIT_FAILURE);
79 }
80
81 const auto &LineInfo =
82 InliningInfo->getFrame(InliningInfo->getNumberOfFrames() - 1);
83
84 // Print the inlining symbolisation of this instruction.
85 for (uint32_t i = 0; i < InliningInfo->getNumberOfFrames(); ++i) {
86 const auto &Line = InliningInfo->getFrame(i);
87 outs() << " " << format_hex(Address, 2) << " = " << Line.FileName << ":"
88 << Line.Line << ":" << Line.Column << " (" << Line.FunctionName
89 << ")\n";
90 }
91
92 if (!SpecialCaseList) {
93 if (CFIProtected)
94 ExpectedProtected++;
95 else
96 UnexpectedUnprotected++;
97 continue;
98 }
99
100 bool MatchesBlacklistRule = false;
101 if (SpecialCaseList->inSection("cfi-icall", "src", LineInfo.FileName) ||
102 SpecialCaseList->inSection("cfi-vcall", "src", LineInfo.FileName)) {
103 outs() << "BLACKLIST MATCH, 'src'\n";
104 MatchesBlacklistRule = true;
105 }
106
107 if (SpecialCaseList->inSection("cfi-icall", "fun", LineInfo.FunctionName) ||
108 SpecialCaseList->inSection("cfi-vcall", "fun", LineInfo.FunctionName)) {
109 outs() << "BLACKLIST MATCH, 'fun'\n";
110 MatchesBlacklistRule = true;
111 }
112
113 if (MatchesBlacklistRule) {
114 if (CFIProtected) {
115 UnexpectedProtected++;
116 outs() << "====> Unexpected Protected\n";
117 } else {
118 ExpectedUnprotected++;
119 outs() << "====> Expected Unprotected\n";
120 }
121 } else {
122 if (CFIProtected) {
123 ExpectedProtected++;
124 outs() << "====> Expected Protected\n";
125 } else {
126 UnexpectedUnprotected++;
127 outs() << "====> Unexpected Unprotected\n";
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000128 }
129 }
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +0000130 }
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000131
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000132 uint64_t IndirectCFInstructions = ExpectedProtected + UnexpectedProtected +
133 ExpectedUnprotected + UnexpectedUnprotected;
134
135 if (IndirectCFInstructions == 0)
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000136 outs() << "No indirect CF instructions found.\n";
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000137
138 outs() << formatv("Expected Protected: {0} ({1:P})\n"
139 "Unexpected Protected: {2} ({3:P})\n"
140 "Expected Unprotected: {4} ({5:P})\n"
141 "Unexpected Unprotected (BAD): {6} ({7:P})\n",
142 ExpectedProtected,
143 ((double)ExpectedProtected) / IndirectCFInstructions,
144 UnexpectedProtected,
145 ((double)UnexpectedProtected) / IndirectCFInstructions,
146 ExpectedUnprotected,
147 ((double)ExpectedUnprotected) / IndirectCFInstructions,
148 UnexpectedUnprotected,
149 ((double)UnexpectedUnprotected) / IndirectCFInstructions);
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +0000150}
151
152int main(int argc, char **argv) {
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000153 cl::ParseCommandLineOptions(
154 argc, argv,
155 "Identifies whether Control Flow Integrity protects all indirect control "
156 "flow instructions in the provided object file, DSO or binary.\nNote: "
157 "Anything statically linked into the provided file *must* be compiled "
158 "with '-g'. This can be relaxed through the '--ignore-dwarf' flag.");
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +0000159
160 InitializeAllTargetInfos();
161 InitializeAllTargetMCs();
162 InitializeAllAsmParsers();
163 InitializeAllDisassemblers();
164
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000165 std::unique_ptr<SpecialCaseList> SpecialCaseList;
166 if (BlacklistFilename != "-") {
167 std::string Error;
168 SpecialCaseList = SpecialCaseList::create({BlacklistFilename}, Error);
169 if (!SpecialCaseList) {
170 errs() << "Failed to get blacklist: " << Error << "\n";
171 exit(EXIT_FAILURE);
172 }
173 }
174
Mitch Phillips7db6f7a2017-10-31 23:20:05 +0000175 FileAnalysis Analysis = ExitOnErr(FileAnalysis::Create(InputFilename));
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000176 printIndirectCFInstructions(Analysis, SpecialCaseList.get());
Vlad Tsyrklevich31b45312017-09-20 20:38:14 +0000177
178 return EXIT_SUCCESS;
179}