llvm.org GIT mirror llvm / bbb3280
Introduce the llvm-cfi-verify tool (resubmission of D37937). Summary: Resubmission of D37937. Fixed i386 target building (conversion from std::size_t& to uint64_t& failed). Fixed documentation warning failure about docs/CFIVerify.rst not being in the tree. Reviewers: vlad.tsyrklevich Reviewed By: vlad.tsyrklevich Patch by Mitch Phillips Subscribers: sbc100, mgorny, pcc, llvm-commits, kcc Differential Revision: https://reviews.llvm.org/D38089 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@313809 91177308-0d34-0410-b5e6-96231b3b80d8 Vlad Tsyrklevich 2 years ago
6 changed file(s) with 375 addition(s) and 1 deletion(s). Raw diff Collapse all Expand all
0 ==============================================
1 Control Flow Verification Tool Design Document
2 ==============================================
3
4 .. contents::
5 :local:
6
7 Objective
8 =========
9
10 This document provides an overview of an external tool to verify the protection
11 mechanisms implemented by Clang's *Control Flow Integrity* (CFI) schemes
12 (``-fsanitize=cfi``). This tool, provided a binary or DSO, should infer whether
13 indirect control flow operations are protected by CFI, and should output these
14 results in a human-readable form.
15
16 This tool should also be added as part of Clang's continuous integration testing
17 framework, where modifications to the compiler ensure that CFI protection
18 schemes are still present in the final binary.
19
20 Location
21 ========
22
23 This tool will be present as a part of the LLVM toolchain, and will reside in
24 the "/llvm/tools/llvm-cfi-verify" directory, relative to the LLVM trunk. It will
25 be tested in two methods:
26
27 - Unit tests to validate code sections, present in "/llvm/unittests/llvm-cfi-
28 verify".
29 - Integration tests, present in "/llvm/tools/clang/test/LLVMCFIVerify". These
30 integration tests are part of clang as part of a continuous integration
31 framework, ensuring updates to the compiler that reduce CFI coverage on
32 indirect control flow instructions are identified.
33
34 Background
35 ==========
36
37 This tool will continuously validate that CFI directives are properly
38 implemented around all indirect control flows by analysing the output machine
39 code. The analysis of machine code is important as it ensures that any bugs
40 present in linker or compiler do not subvert CFI protections in the final
41 shipped binary.
42
43 Unprotected indirect control flow instructions will be flagged for manual
44 review. These unexpected control flows may simply have not been accounted for in
45 the compiler implementation of CFI (e.g. indirect jumps to facilitate switch
46 statements may not be fully protected).
47
48 It may be possible in the future to extend this tool to flag unnecessary CFI
49 directives (e.g. CFI directives around a static call to a non-polymorphic base
50 type). This type of directive has no security implications, but may present
51 performance impacts.
52
53 Design Ideas
54 ============
55
56 This tool will disassemble binaries and DSO's from their machine code format and
57 analyse the disassembled machine code. The tool will inspect virtual calls and
58 indirect function calls. This tool will also inspect indirect jumps, as inlined
59 functions and jump tables should also be subject to CFI protections. Non-virtual
60 calls (``-fsanitize=cfi-nvcall``) and cast checks (``-fsanitize=cfi-*cast*``)
61 are not implemented due to a lack of information provided by the bytecode.
62
63 The tool would operate by searching for indirect control flow instructions in
64 the disassembly. A control flow graph would be generated from a small buffer of
65 the instructions surrounding the 'target' control flow instruction. If the
66 target instruction is branched-to, the fallthrough of the branch should be the
67 CFI trap (on x86, this is a ``ud2`` instruction). If the target instruction is
68 the fallthrough (i.e. immediately succeeds) of a conditional jump, the
69 conditional jump target should be the CFI trap. If an indirect control flow
70 instruction does not conform to one of these formats, the target will be noted
71 as being CFI-unprotected.
72
73 Note that in the second case outlined above (where the target instruction is the
74 fallthrough of a conditional jump), if the target represents a vcall that takes
75 arguments, these arguments may be pushed to the stack after the branch but
76 before the target instruction. In these cases, a secondary 'spill graph' in
77 constructed, to ensure the register argument used by the indirect jump/call is
78 not spilled from the stack at any point in the interim period. If there are no
79 spills that affect the target register, the target is marked as CFI-protected.
80
81 Other Design Notes
82 ~~~~~~~~~~~~~~~~~~
83
84 Only machine code sections that are marked as executable will be subject to this
85 analysis. Non-executable sections do not require analysis as any execution
86 present in these sections has already violated the control flow integrity.
87
88 Suitable extensions may be made at a later date to include anaylsis for indirect
89 control flow operations across DSO boundaries. Currently, these CFI features are
90 only experimental with an unstable ABI, making them unsuitable for analysis.
158158 misunderstood instruction.
159159
160160 :doc:`Frontend/PerformanceTips`
161 A collection of tips for frontend authors on how to generate IR
161 A collection of tips for frontend authors on how to generate IR
162162 which LLVM is able to effectively optimize.
163163
164164 :doc:`Docker`
280280 XRayExample
281281 XRayFDRFormat
282282 PDB/index
283 CFIVerify
283284
284285 :doc:`WritingAnLLVMPass`
285286 Information on how to write LLVM transformations and analyses.
409410
410411 :doc:`The Microsoft PDB File Format `
411412 A detailed description of the Microsoft PDB (Program Database) file format.
413
414 :doc:`CFIVerify`
415 A description of the verification tool for Control Flow Integrity.
412416
413417 Development Process Documentation
414418 =================================
2424 llvm-as
2525 llvm-bcanalyzer
2626 llvm-cat
27 llvm-cfi-verify
2728 llvm-cov
2829 llvm-cvtres
2930 llvm-diff
0 set(LLVM_LINK_COMPONENTS
1 AllTargetsAsmPrinters
2 AllTargetsAsmParsers
3 AllTargetsDescs
4 AllTargetsDisassemblers
5 AllTargetsInfos
6 MC
7 MCParser
8 Object
9 Support
10 )
11
12 add_llvm_tool(llvm-cfi-verify
13 llvm-cfi-verify.cpp
14 )
0 ;===- ./tools/llvm-cfi-verify/LLVMBuild.txt --------------------*- Conf -*--===;
1 ;
2 ; The LLVM Compiler Infrastructure
3 ;
4 ; This file is distributed under the University of Illinois Open Source
5 ; License. See LICENSE.TXT for details.
6 ;
7 ;===------------------------------------------------------------------------===;
8 ;
9 ; This is an LLVMBuild description file for the components in this subdirectory.
10 ;
11 ; For more information on the LLVMBuild system, please see:
12 ;
13 ; http://llvm.org/docs/LLVMBuild.html
14 ;
15 ;===------------------------------------------------------------------------===;
16
17 [component_0]
18 type = Tool
19 name = llvm-cfi-verify
20 parent = Tools
21 required_libraries = MC MCDisassembler MCParser Support all-targets
0 //===-- llvm-cfi-verify.cpp - CFI Verification tool for LLVM --------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
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.
12 //
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.
16 //
17 //===----------------------------------------------------------------------===//
18
19 #include "llvm/MC/MCAsmInfo.h"
20 #include "llvm/MC/MCContext.h"
21 #include "llvm/MC/MCDisassembler/MCDisassembler.h"
22 #include "llvm/MC/MCInst.h"
23 #include "llvm/MC/MCInstPrinter.h"
24 #include "llvm/MC/MCInstrAnalysis.h"
25 #include "llvm/MC/MCInstrDesc.h"
26 #include "llvm/MC/MCInstrInfo.h"
27 #include "llvm/MC/MCObjectFileInfo.h"
28 #include "llvm/MC/MCRegisterInfo.h"
29 #include "llvm/MC/MCSubtargetInfo.h"
30 #include "llvm/Object/Binary.h"
31 #include "llvm/Object/COFF.h"
32 #include "llvm/Object/ObjectFile.h"
33 #include "llvm/Support/Casting.h"
34 #include "llvm/Support/CommandLine.h"
35 #include "llvm/Support/MemoryBuffer.h"
36 #include "llvm/Support/TargetRegistry.h"
37 #include "llvm/Support/TargetSelect.h"
38 #include "llvm/Support/raw_ostream.h"
39
40 #include
41 #include
42
43 using namespace llvm;
44 using namespace llvm::object;
45
46 cl::opt ArgDumpSymbols("sym", cl::desc("Dump the symbol table."));
47 cl::opt InputFilename(cl::Positional, cl::desc(""),
48 cl::Required);
49
50 static void printSymbols(const ObjectFile *Object) {
51 for (const SymbolRef &Symbol : Object->symbols()) {
52 outs() << "Symbol [" << format_hex_no_prefix(Symbol.getValue(), 2)
53 << "] = ";
54
55 auto SymbolName = Symbol.getName();
56 if (SymbolName)
57 outs() << *SymbolName;
58 else
59 outs() << "UNKNOWN";
60
61 if (Symbol.getFlags() & SymbolRef::SF_Hidden)
62 outs() << " .hidden";
63
64 outs() << " (Section = ";
65
66 auto SymbolSection = Symbol.getSection();
67 if (SymbolSection) {
68 StringRef SymbolSectionName;
69 if ((*SymbolSection)->getName(SymbolSectionName))
70 outs() << "UNKNOWN)";
71 else
72 outs() << SymbolSectionName << ")";
73 } else {
74 outs() << "N/A)";
75 }
76
77 outs() << "\n";
78 }
79 }
80
81 int main(int argc, char **argv) {
82 cl::ParseCommandLineOptions(argc, argv);
83
84 InitializeAllTargetInfos();
85 InitializeAllTargetMCs();
86 InitializeAllAsmParsers();
87 InitializeAllDisassemblers();
88
89 Expected> BinaryOrErr = createBinary(InputFilename);
90 if (!BinaryOrErr) {
91 errs() << "Failed to open file.\n";
92 return EXIT_FAILURE;
93 }
94
95 Binary &Binary = *BinaryOrErr.get().getBinary();
96 ObjectFile *Object = dyn_cast(&Binary);
97 if (!Object) {
98 errs() << "Disassembling of non-objects not currently supported.\n";
99 return EXIT_FAILURE;
100 }
101
102 Triple TheTriple = Object->makeTriple();
103 std::string TripleName = TheTriple.getTriple();
104 std::string ArchName = "";
105 std::string ErrorString;
106
107 const Target *TheTarget =
108 TargetRegistry::lookupTarget(ArchName, TheTriple, ErrorString);
109
110 if (!TheTarget) {
111 errs() << "Couldn't find target \"" << TheTriple.getTriple()
112 << "\", failed with error: " << ErrorString << ".\n";
113 return EXIT_FAILURE;
114 }
115
116 SubtargetFeatures Features = Object->getFeatures();
117
118 std::unique_ptr RegisterInfo(
119 TheTarget->createMCRegInfo(TripleName));
120 if (!RegisterInfo) {
121 errs() << "Failed to initialise RegisterInfo.\n";
122 return EXIT_FAILURE;
123 }
124
125 std::unique_ptr AsmInfo(
126 TheTarget->createMCAsmInfo(*RegisterInfo, TripleName));
127 if (!AsmInfo) {
128 errs() << "Failed to initialise AsmInfo.\n";
129 return EXIT_FAILURE;
130 }
131
132 std::string MCPU = "";
133 std::unique_ptr SubtargetInfo(
134 TheTarget->createMCSubtargetInfo(TripleName, MCPU, Features.getString()));
135 if (!SubtargetInfo) {
136 errs() << "Failed to initialise SubtargetInfo.\n";
137 return EXIT_FAILURE;
138 }
139
140 std::unique_ptr MII(TheTarget->createMCInstrInfo());
141 if (!MII) {
142 errs() << "Failed to initialise MII.\n";
143 return EXIT_FAILURE;
144 }
145
146 MCObjectFileInfo MOFI;
147 MCContext Context(AsmInfo.get(), RegisterInfo.get(), &MOFI);
148
149 std::unique_ptr Disassembler(
150 TheTarget->createMCDisassembler(*SubtargetInfo, Context));
151
152 if (!Disassembler) {
153 errs() << "No disassembler available for target.";
154 return EXIT_FAILURE;
155 }
156
157 std::unique_ptr MIA(
158 TheTarget->createMCInstrAnalysis(MII.get()));
159
160 std::unique_ptr Printer(
161 TheTarget->createMCInstPrinter(TheTriple, AsmInfo->getAssemblerDialect(),
162 *AsmInfo, *MII, *RegisterInfo));
163
164 if (ArgDumpSymbols)
165 printSymbols(Object);
166
167 for (const SectionRef &Section : Object->sections()) {
168 outs() << "Section [" << format_hex_no_prefix(Section.getAddress(), 2)
169 << "] = ";
170 StringRef SectionName;
171
172 if (Section.getName(SectionName))
173 outs() << "UNKNOWN.\n";
174 else
175 outs() << SectionName << "\n";
176
177 StringRef SectionContents;
178 if (Section.getContents(SectionContents)) {
179 errs() << "Failed to retrieve section contents.\n";
180 return EXIT_FAILURE;
181 }
182
183 MCInst Instruction;
184 uint64_t InstructionSize;
185
186 ArrayRef SectionBytes((const uint8_t *)SectionContents.data(),
187 Section.getSize());
188
189 for (uint64_t Byte = 0; Byte < Section.getSize();) {
190 bool BadInstruction = false;
191
192 // Disassemble the instruction.
193 if (Disassembler->getInstruction(
194 Instruction, InstructionSize, SectionBytes.drop_front(Byte), 0,
195 nulls(), outs()) != MCDisassembler::Success) {
196 BadInstruction = true;
197 }
198
199 Byte += InstructionSize;
200
201 if (BadInstruction)
202 continue;
203
204 // Skip instructions that do not affect the control flow.
205 const auto &InstrDesc = MII->get(Instruction.getOpcode());
206 if (!InstrDesc.mayAffectControlFlow(Instruction, *RegisterInfo))
207 continue;
208
209 // Skip instructions that do not operate on register operands.
210 bool UsesRegisterOperand = false;
211 for (const auto &Operand : Instruction) {
212 if (Operand.isReg())
213 UsesRegisterOperand = true;
214 }
215
216 if (!UsesRegisterOperand)
217 continue;
218
219 // Print the instruction address.
220 outs() << " "
221 << format_hex(Section.getAddress() + Byte - InstructionSize, 2)
222 << ": ";
223
224 // Print the instruction bytes.
225 for (uint64_t i = 0; i < InstructionSize; ++i) {
226 outs() << format_hex_no_prefix(SectionBytes[Byte - InstructionSize + i],
227 2)
228 << " ";
229 }
230
231 // Print the instruction.
232 outs() << " | " << MII->getName(Instruction.getOpcode()) << " ";
233 Instruction.dump_pretty(outs(), Printer.get());
234
235 outs() << "\n";
236 }
237 }
238
239 return EXIT_SUCCESS;
240 }