121
|
1 //===- llvm/unittests/llvm-cfi-verify/GraphBuilder.cpp --------------===//
|
|
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 #include "../tools/llvm-cfi-verify/lib/GraphBuilder.h"
|
|
11 #include "../tools/llvm-cfi-verify/lib/FileAnalysis.h"
|
|
12 #include "gmock/gmock.h"
|
|
13 #include "gtest/gtest.h"
|
|
14
|
|
15 #include "llvm/BinaryFormat/ELF.h"
|
|
16 #include "llvm/MC/MCAsmInfo.h"
|
|
17 #include "llvm/MC/MCContext.h"
|
|
18 #include "llvm/MC/MCDisassembler/MCDisassembler.h"
|
|
19 #include "llvm/MC/MCInst.h"
|
|
20 #include "llvm/MC/MCInstPrinter.h"
|
|
21 #include "llvm/MC/MCInstrAnalysis.h"
|
|
22 #include "llvm/MC/MCInstrDesc.h"
|
|
23 #include "llvm/MC/MCInstrInfo.h"
|
|
24 #include "llvm/MC/MCObjectFileInfo.h"
|
|
25 #include "llvm/MC/MCRegisterInfo.h"
|
|
26 #include "llvm/MC/MCSubtargetInfo.h"
|
|
27 #include "llvm/Object/Binary.h"
|
|
28 #include "llvm/Object/COFF.h"
|
|
29 #include "llvm/Object/ELFObjectFile.h"
|
|
30 #include "llvm/Object/ObjectFile.h"
|
|
31 #include "llvm/Support/Casting.h"
|
|
32 #include "llvm/Support/CommandLine.h"
|
|
33 #include "llvm/Support/Error.h"
|
|
34 #include "llvm/Support/MemoryBuffer.h"
|
|
35 #include "llvm/Support/TargetRegistry.h"
|
|
36 #include "llvm/Support/TargetSelect.h"
|
|
37 #include "llvm/Support/raw_ostream.h"
|
|
38
|
|
39 #include <cstdlib>
|
|
40 #include <sstream>
|
|
41
|
|
42 using Instr = ::llvm::cfi_verify::FileAnalysis::Instr;
|
|
43 using ::testing::AllOf;
|
|
44 using ::testing::Each;
|
|
45 using ::testing::ElementsAre;
|
|
46 using ::testing::Eq;
|
|
47 using ::testing::Field;
|
|
48 using ::testing::IsEmpty;
|
|
49 using ::testing::Matches;
|
|
50 using ::testing::Pair;
|
|
51 using ::testing::PrintToString;
|
|
52 using ::testing::Property;
|
|
53 using ::testing::SizeIs;
|
|
54 using ::testing::UnorderedElementsAre;
|
|
55 using ::testing::Value;
|
|
56
|
|
57 namespace llvm {
|
|
58 namespace cfi_verify {
|
|
59 // Printing helpers for gtest.
|
|
60 std::string HexStringifyContainer(const std::vector<uint64_t> &C) {
|
|
61 std::stringstream Stream;
|
|
62 if (C.empty()) {
|
|
63 return "{ }";
|
|
64 }
|
|
65
|
|
66 Stream << "{ ";
|
|
67 const auto &LastElemIt = std::end(C) - 1;
|
|
68
|
|
69 for (auto It = std::begin(C); It != LastElemIt; ++It) {
|
|
70 Stream << "0x" << std::hex << *It << ", ";
|
|
71 }
|
|
72 Stream << "0x" << std::hex << *LastElemIt << " }";
|
|
73 return Stream.str();
|
|
74 }
|
|
75
|
|
76 void PrintTo(const ConditionalBranchNode &BranchNode, ::std::ostream *os) {
|
|
77 *os << "ConditionalBranchNode<Address: 0x" << std::hex << BranchNode.Address
|
|
78 << ", Target: 0x" << BranchNode.Target << ", Fallthrough: 0x"
|
|
79 << BranchNode.Fallthrough
|
|
80 << ", CFIProtection: " << BranchNode.CFIProtection << ">";
|
|
81 }
|
|
82
|
|
83 void PrintTo(const GraphResult &Result, ::std::ostream *os) {
|
|
84 *os << "Result BaseAddress: 0x" << std::hex << Result.BaseAddress << "\n";
|
|
85
|
|
86 if (Result.ConditionalBranchNodes.empty())
|
|
87 *os << " (No conditional branch nodes)\n";
|
|
88
|
|
89 for (const auto &Node : Result.ConditionalBranchNodes) {
|
|
90 *os << " ";
|
|
91 PrintTo(Node, os);
|
|
92 *os << "\n Fallthrough Path: " << std::hex
|
|
93 << HexStringifyContainer(Result.flattenAddress(Node.Fallthrough))
|
|
94 << "\n";
|
|
95 *os << " Target Path: " << std::hex
|
|
96 << HexStringifyContainer(Result.flattenAddress(Node.Target)) << "\n";
|
|
97 }
|
|
98
|
|
99 if (Result.OrphanedNodes.empty())
|
|
100 *os << " (No orphaned nodes)";
|
|
101
|
|
102 for (const auto &Orphan : Result.OrphanedNodes) {
|
|
103 *os << " Orphan (0x" << std::hex << Orphan
|
|
104 << ") Path: " << HexStringifyContainer(Result.flattenAddress(Orphan))
|
|
105 << "\n";
|
|
106 }
|
|
107 }
|
|
108
|
|
109 namespace {
|
|
110 class ELFx86TestFileAnalysis : public FileAnalysis {
|
|
111 public:
|
|
112 ELFx86TestFileAnalysis()
|
|
113 : FileAnalysis(Triple("x86_64--"), SubtargetFeatures()) {}
|
|
114
|
|
115 // Expose this method publicly for testing.
|
|
116 void parseSectionContents(ArrayRef<uint8_t> SectionBytes,
|
|
117 uint64_t SectionAddress) {
|
|
118 FileAnalysis::parseSectionContents(SectionBytes, SectionAddress);
|
|
119 }
|
|
120
|
|
121 Error initialiseDisassemblyMembers() {
|
|
122 return FileAnalysis::initialiseDisassemblyMembers();
|
|
123 }
|
|
124 };
|
|
125
|
|
126 class BasicGraphBuilderTest : public ::testing::Test {
|
|
127 protected:
|
|
128 virtual void SetUp() {
|
|
129 SuccessfullyInitialised = true;
|
|
130 if (auto Err = Analysis.initialiseDisassemblyMembers()) {
|
|
131 handleAllErrors(std::move(Err), [&](const UnsupportedDisassembly &E) {
|
|
132 SuccessfullyInitialised = false;
|
|
133 outs()
|
|
134 << "Note: CFIVerifyTests are disabled due to lack of x86 support "
|
|
135 "on this build.\n";
|
|
136 });
|
|
137 }
|
|
138 }
|
|
139
|
|
140 bool SuccessfullyInitialised;
|
|
141 ELFx86TestFileAnalysis Analysis;
|
|
142 };
|
|
143
|
|
144 MATCHER_P2(HasPath, Result, Matcher, "has path " + PrintToString(Matcher)) {
|
|
145 const auto &Path = Result.flattenAddress(arg);
|
|
146 *result_listener << "the path is " << PrintToString(Path);
|
|
147 return Matches(Matcher)(Path);
|
|
148 }
|
|
149
|
|
150 TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestSinglePathFallthroughUd2) {
|
|
151 if (!SuccessfullyInitialised)
|
|
152 return;
|
|
153 Analysis.parseSectionContents(
|
|
154 {
|
|
155 0x75, 0x02, // 0: jne 4 [+2]
|
|
156 0x0f, 0x0b, // 2: ud2
|
|
157 0xff, 0x10, // 4: callq *(%rax)
|
|
158 },
|
|
159 0xDEADBEEF);
|
|
160 const auto Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 4);
|
|
161
|
|
162 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
|
|
163 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));
|
|
164 EXPECT_THAT(Result.ConditionalBranchNodes,
|
|
165 Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));
|
|
166 EXPECT_THAT(
|
|
167 Result.ConditionalBranchNodes,
|
|
168 Contains(AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
|
|
169 Field(&ConditionalBranchNode::Target,
|
|
170 HasPath(Result, ElementsAre(0xDEADBEEF + 4))),
|
|
171 Field(&ConditionalBranchNode::Fallthrough,
|
|
172 HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))
|
|
173 << PrintToString(Result);
|
|
174 }
|
|
175
|
|
176 TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestSinglePathJumpUd2) {
|
|
177 if (!SuccessfullyInitialised)
|
|
178 return;
|
|
179 Analysis.parseSectionContents(
|
|
180 {
|
|
181 0x75, 0x02, // 0: jne 4 [+2]
|
|
182 0xff, 0x10, // 2: callq *(%rax)
|
|
183 0x0f, 0x0b, // 4: ud2
|
|
184 },
|
|
185 0xDEADBEEF);
|
|
186 const auto Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 2);
|
|
187
|
|
188 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
|
|
189 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));
|
|
190 EXPECT_THAT(Result.ConditionalBranchNodes,
|
|
191 Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));
|
|
192 EXPECT_THAT(
|
|
193 Result.ConditionalBranchNodes,
|
|
194 Contains(AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
|
|
195 Field(&ConditionalBranchNode::Target,
|
|
196 HasPath(Result, ElementsAre(0xDEADBEEF + 4))),
|
|
197 Field(&ConditionalBranchNode::Fallthrough,
|
|
198 HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))
|
|
199 << PrintToString(Result);
|
|
200 }
|
|
201
|
|
202 TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestDualPathDualUd2) {
|
|
203 if (!SuccessfullyInitialised)
|
|
204 return;
|
|
205 Analysis.parseSectionContents(
|
|
206 {
|
|
207 0x75, 0x03, // 0: jne 5 [+3]
|
|
208 0x90, // 2: nop
|
|
209 0xff, 0x10, // 3: callq *(%rax)
|
|
210 0x0f, 0x0b, // 5: ud2
|
|
211 0x75, 0xf9, // 7: jne 2 [-7]
|
|
212 0x0f, 0x0b, // 9: ud2
|
|
213 },
|
|
214 0xDEADBEEF);
|
|
215 const auto Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 3);
|
|
216
|
|
217 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
|
|
218 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(2));
|
|
219 EXPECT_THAT(Result.ConditionalBranchNodes,
|
|
220 Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));
|
|
221 EXPECT_THAT(
|
|
222 Result.ConditionalBranchNodes,
|
|
223 Contains(AllOf(
|
|
224 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
|
|
225 Field(&ConditionalBranchNode::Fallthrough,
|
|
226 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))),
|
|
227 Field(&ConditionalBranchNode::Target,
|
|
228 HasPath(Result, ElementsAre(0xDEADBEEF + 5))))))
|
|
229 << PrintToString(Result);
|
|
230 EXPECT_THAT(
|
|
231 Result.ConditionalBranchNodes,
|
|
232 Contains(AllOf(
|
|
233 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF + 7)),
|
|
234 Field(&ConditionalBranchNode::Fallthrough,
|
|
235 HasPath(Result, ElementsAre(0xDEADBEEF + 9))),
|
|
236 Field(&ConditionalBranchNode::Target,
|
|
237 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))))))
|
|
238 << PrintToString(Result);
|
|
239 }
|
|
240
|
|
241 TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestDualPathSingleUd2) {
|
|
242 if (!SuccessfullyInitialised)
|
|
243 return;
|
|
244 Analysis.parseSectionContents(
|
|
245 {
|
|
246 0x75, 0x05, // 0: jne 7 [+5]
|
|
247 0x90, // 2: nop
|
|
248 0xff, 0x10, // 3: callq *(%rax)
|
|
249 0x75, 0xfb, // 5: jne 2 [-5]
|
|
250 0x0f, 0x0b, // 7: ud2
|
|
251 },
|
|
252 0xDEADBEEF);
|
|
253 GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 3);
|
|
254
|
|
255 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
|
|
256 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(2));
|
|
257 EXPECT_THAT(Result.ConditionalBranchNodes,
|
|
258 Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));
|
|
259 EXPECT_THAT(
|
|
260 Result.ConditionalBranchNodes,
|
|
261 Contains(AllOf(
|
|
262 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
|
|
263 Field(&ConditionalBranchNode::Fallthrough,
|
|
264 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))),
|
|
265 Field(&ConditionalBranchNode::Target,
|
|
266 HasPath(Result, ElementsAre(0xDEADBEEF + 7))))))
|
|
267 << PrintToString(Result);
|
|
268 EXPECT_THAT(
|
|
269 Result.ConditionalBranchNodes,
|
|
270 Contains(AllOf(
|
|
271 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF + 5)),
|
|
272 Field(&ConditionalBranchNode::Fallthrough,
|
|
273 HasPath(Result, ElementsAre(0xDEADBEEF + 7))),
|
|
274 Field(&ConditionalBranchNode::Target,
|
|
275 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))))))
|
|
276 << PrintToString(Result);
|
|
277 }
|
|
278
|
|
279 TEST_F(BasicGraphBuilderTest, BuildFlowGraphFailures) {
|
|
280 if (!SuccessfullyInitialised)
|
|
281 return;
|
|
282 Analysis.parseSectionContents(
|
|
283 {
|
|
284 0x90, // 0: nop
|
|
285 0x75, 0xfe, // 1: jne 1 [-2]
|
|
286 },
|
|
287 0xDEADBEEF);
|
|
288 GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF);
|
|
289 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
|
|
290 EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
|
|
291
|
|
292 Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 1);
|
|
293 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
|
|
294 EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
|
|
295
|
|
296 Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADC0DE);
|
|
297 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
|
|
298 EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
|
|
299 }
|
|
300
|
|
301 TEST_F(BasicGraphBuilderTest, BuildFlowGraphNoXrefs) {
|
|
302 if (!SuccessfullyInitialised)
|
|
303 return;
|
|
304 Analysis.parseSectionContents(
|
|
305 {
|
|
306 0xeb, 0xfe, // 0: jmp 0 [-2]
|
|
307 0xff, 0x10, // 2: callq *(%rax)
|
|
308 },
|
|
309 0xDEADBEEF);
|
|
310 GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 2);
|
|
311 EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
|
|
312 EXPECT_THAT(Result.OrphanedNodes, ElementsAre(0xDEADBEEF + 2));
|
|
313 EXPECT_THAT(Result.IntermediateNodes, IsEmpty());
|
|
314 }
|
|
315
|
|
316 TEST_F(BasicGraphBuilderTest, BuildFlowGraphConditionalInfiniteLoop) {
|
|
317 if (!SuccessfullyInitialised)
|
|
318 return;
|
|
319 Analysis.parseSectionContents(
|
|
320 {
|
|
321 0x75, 0xfe, // 0: jne 0 [-2]
|
|
322 0xff, 0x10, // 2: callq *(%rax)
|
|
323 },
|
|
324 0xDEADBEEF);
|
|
325 GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 2);
|
|
326 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
|
|
327 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));
|
|
328 EXPECT_THAT(
|
|
329 Result.ConditionalBranchNodes,
|
|
330 Each(AllOf(Field(&ConditionalBranchNode::CFIProtection, Eq(false)),
|
|
331 Field(&ConditionalBranchNode::Target,
|
|
332 HasPath(Result, ElementsAre(0xDEADBEEF))),
|
|
333 Field(&ConditionalBranchNode::Fallthrough,
|
|
334 HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))
|
|
335 << PrintToString(Result);
|
|
336 }
|
|
337
|
|
338 TEST_F(BasicGraphBuilderTest, BuildFlowGraphUnconditionalInfiniteLoop) {
|
|
339 if (!SuccessfullyInitialised)
|
|
340 return;
|
|
341 Analysis.parseSectionContents(
|
|
342 {
|
|
343 0x75, 0x02, // 0: jne 4 [+2]
|
|
344 0xeb, 0xfc, // 2: jmp 0 [-4]
|
|
345 0xff, 0x10, // 4: callq *(%rax)
|
|
346 },
|
|
347 0xDEADBEEF);
|
|
348 GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 4);
|
|
349 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
|
|
350 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));
|
|
351 EXPECT_THAT(
|
|
352 Result.ConditionalBranchNodes,
|
|
353 Contains(
|
|
354 AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
|
|
355 Field(&ConditionalBranchNode::Fallthrough,
|
|
356 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF))),
|
|
357 Field(&ConditionalBranchNode::Target,
|
|
358 HasPath(Result, ElementsAre(0xDEADBEEF + 4))))))
|
|
359 << PrintToString(Result);
|
|
360 }
|
|
361
|
|
362 TEST_F(BasicGraphBuilderTest, BuildFlowGraphNoFlowsToIndirection) {
|
|
363 if (!SuccessfullyInitialised)
|
|
364 return;
|
|
365 Analysis.parseSectionContents(
|
|
366 {
|
|
367 0x75, 0x00, // 0: jne 2 [+0]
|
|
368 0xeb, 0xfc, // 2: jmp 0 [-4]
|
|
369 0xff, 0x10, // 4: callq *(%rax)
|
|
370 },
|
|
371 0xDEADBEEF);
|
|
372 GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 4);
|
|
373 EXPECT_THAT(Result.OrphanedNodes, ElementsAre(0xDEADBEEF + 4));
|
|
374 EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
|
|
375 }
|
|
376
|
|
377 TEST_F(BasicGraphBuilderTest, BuildFlowGraphLengthExceededUpwards) {
|
|
378 if (!SuccessfullyInitialised)
|
|
379 return;
|
|
380 Analysis.parseSectionContents(
|
|
381 {
|
|
382 0x75, 0x06, // 0: jne 8 [+6]
|
|
383 0x90, // 2: nop
|
|
384 0x90, // 3: nop
|
|
385 0x90, // 4: nop
|
|
386 0x90, // 5: nop
|
|
387 0xff, 0x10, // 6: callq *(%rax)
|
|
388 0x0f, 0x0b, // 8: ud2
|
|
389 },
|
|
390 0xDEADBEEF);
|
|
391 uint64_t PrevSearchLengthForConditionalBranch =
|
|
392 SearchLengthForConditionalBranch;
|
|
393 SearchLengthForConditionalBranch = 2;
|
|
394
|
|
395 GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 6);
|
|
396 EXPECT_THAT(Result.OrphanedNodes, SizeIs(1));
|
|
397 EXPECT_THAT(Result.OrphanedNodes,
|
|
398 Each(HasPath(Result, ElementsAre(0xDEADBEEF + 4, 0xDEADBEEF + 5,
|
|
399 0xDEADBEEF + 6))))
|
|
400 << PrintToString(Result);
|
|
401 EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
|
|
402
|
|
403 SearchLengthForConditionalBranch = PrevSearchLengthForConditionalBranch;
|
|
404 }
|
|
405
|
|
406 TEST_F(BasicGraphBuilderTest, BuildFlowGraphLengthExceededDownwards) {
|
|
407 if (!SuccessfullyInitialised)
|
|
408 return;
|
|
409 Analysis.parseSectionContents(
|
|
410 {
|
|
411 0x75, 0x02, // 0: jne 4 [+2]
|
|
412 0xff, 0x10, // 2: callq *(%rax)
|
|
413 0x90, // 4: nop
|
|
414 0x90, // 5: nop
|
|
415 0x90, // 6: nop
|
|
416 0x90, // 7: nop
|
|
417 0x0f, 0x0b, // 8: ud2
|
|
418 },
|
|
419 0xDEADBEEF);
|
|
420 uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;
|
|
421 SearchLengthForUndef = 2;
|
|
422
|
|
423 GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 2);
|
|
424 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
|
|
425 EXPECT_THAT(
|
|
426 Result.ConditionalBranchNodes,
|
|
427 Each(AllOf(
|
|
428 Field(&ConditionalBranchNode::CFIProtection, Eq(false)),
|
|
429 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
|
|
430 Field(&ConditionalBranchNode::Target,
|
|
431 HasPath(Result, ElementsAre(0xDEADBEEF + 4, 0xDEADBEEF + 5))),
|
|
432 Field(&ConditionalBranchNode::Fallthrough,
|
|
433 HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))
|
|
434 << PrintToString(Result);
|
|
435
|
|
436 SearchLengthForUndef = PrevSearchLengthForUndef;
|
|
437 }
|
|
438
|
|
439 // This test ensures when avoiding doing repeated work we still generate the
|
|
440 // paths correctly. We don't need to recalculate the flow from 0x2 -> 0x3 as it
|
|
441 // should only need to be generated once.
|
|
442 TEST_F(BasicGraphBuilderTest, BuildFlowGraphWithRepeatedWork) {
|
|
443 if (!SuccessfullyInitialised)
|
|
444 return;
|
|
445 Analysis.parseSectionContents(
|
|
446 {
|
|
447 0x75, 0x05, // 0: jne 7 [+5]
|
|
448 0x90, // 2: nop
|
|
449 0xff, 0x10, // 3: callq *(%rax)
|
|
450 0x75, 0xfb, // 5: jne 2 [-5]
|
|
451 0x0f, 0x0b, // 7: ud2
|
|
452 },
|
|
453 0xDEADBEEF);
|
|
454 GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 3);
|
|
455 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
|
|
456 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(2));
|
|
457 EXPECT_THAT(
|
|
458 Result.ConditionalBranchNodes,
|
|
459 Contains(AllOf(
|
|
460 Field(&ConditionalBranchNode::CFIProtection, Eq(true)),
|
|
461 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
|
|
462 Field(&ConditionalBranchNode::Target,
|
|
463 HasPath(Result, ElementsAre(0xDEADBEEF + 7))),
|
|
464 Field(&ConditionalBranchNode::Fallthrough,
|
|
465 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))))))
|
|
466 << PrintToString(Result);
|
|
467 EXPECT_THAT(
|
|
468 Result.ConditionalBranchNodes,
|
|
469 Contains(AllOf(
|
|
470 Field(&ConditionalBranchNode::CFIProtection, Eq(true)),
|
|
471 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF + 5)),
|
|
472 Field(&ConditionalBranchNode::Target,
|
|
473 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))),
|
|
474 Field(&ConditionalBranchNode::Fallthrough,
|
|
475 HasPath(Result, ElementsAre(0xDEADBEEF + 7))))))
|
|
476 << PrintToString(Result);
|
|
477 EXPECT_THAT(Result.IntermediateNodes, SizeIs(1));
|
|
478 EXPECT_THAT(Result.IntermediateNodes,
|
|
479 UnorderedElementsAre(Pair(0xDEADBEEF + 2, 0xDEADBEEF + 3)));
|
|
480 }
|
|
481
|
|
482 TEST_F(BasicGraphBuilderTest, BuildFlowGraphComplexExample) {
|
|
483 if (!SuccessfullyInitialised)
|
|
484 return;
|
|
485 // The following code has this graph:
|
|
486 // +----------+ +--------------+
|
|
487 // | 20 | <--- | 0 |
|
|
488 // +----------+ +--------------+
|
|
489 // | |
|
|
490 // v v
|
|
491 // +----------+ +--------------+
|
|
492 // | 21 | | 2 |
|
|
493 // +----------+ +--------------+
|
|
494 // | |
|
|
495 // v v
|
|
496 // +----------+ +--------------+
|
|
497 // | 22 (ud2) | +-> | 7 |
|
|
498 // +----------+ | +--------------+
|
|
499 // ^ | |
|
|
500 // | | v
|
|
501 // +----------+ | +--------------+
|
|
502 // | 4 | | | 8 |
|
|
503 // +----------+ | +--------------+
|
|
504 // | | |
|
|
505 // v | v
|
|
506 // +----------+ | +--------------+ +------------+
|
|
507 // | 6 | -+ | 9 (indirect) | <- | 13 |
|
|
508 // +----------+ +--------------+ +------------+
|
|
509 // ^ |
|
|
510 // | v
|
|
511 // +--------------+ +------------+
|
|
512 // | 11 | | 15 (error) |
|
|
513 // +--------------+ +------------+
|
|
514 // Or, in image format: https://i.imgur.com/aX5fCoi.png
|
|
515
|
|
516 Analysis.parseSectionContents(
|
|
517 {
|
|
518 0x75, 0x12, // 0: jne 20 [+18]
|
|
519 0xeb, 0x03, // 2: jmp 7 [+3]
|
|
520 0x75, 0x10, // 4: jne 22 [+16]
|
|
521 0x90, // 6: nop
|
|
522 0x90, // 7: nop
|
|
523 0x90, // 8: nop
|
|
524 0xff, 0x10, // 9: callq *(%rax)
|
|
525 0xeb, 0xfc, // 11: jmp 9 [-4]
|
|
526 0x75, 0xfa, // 13: jne 9 [-6]
|
|
527 0xe8, 0x78, 0x56, 0x34, 0x12, // 15: callq OUTOFBOUNDS [+0x12345678]
|
|
528 0x90, // 20: nop
|
|
529 0x90, // 21: nop
|
|
530 0x0f, 0x0b, // 22: ud2
|
|
531 },
|
|
532 0x1000);
|
|
533 uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;
|
|
534 SearchLengthForUndef = 5;
|
|
535
|
|
536 GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0x1000 + 9);
|
|
537
|
|
538 EXPECT_THAT(Result.OrphanedNodes, SizeIs(1));
|
|
539 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(3));
|
|
540
|
|
541 EXPECT_THAT(
|
|
542 Result.OrphanedNodes,
|
|
543 Each(AllOf(Eq(0x1000u + 11),
|
|
544 HasPath(Result, ElementsAre(0x1000 + 11, 0x1000 + 9)))))
|
|
545 << PrintToString(Result);
|
|
546
|
|
547 EXPECT_THAT(Result.ConditionalBranchNodes,
|
|
548 Contains(AllOf(
|
|
549 Field(&ConditionalBranchNode::CFIProtection, Eq(true)),
|
|
550 Field(&ConditionalBranchNode::Address, Eq(0x1000u)),
|
|
551 Field(&ConditionalBranchNode::Target,
|
|
552 HasPath(Result, ElementsAre(0x1000 + 20, 0x1000 + 21,
|
|
553 0x1000 + 22))),
|
|
554 Field(&ConditionalBranchNode::Fallthrough,
|
|
555 HasPath(Result, ElementsAre(0x1000 + 2, 0x1000 + 7,
|
|
556 0x1000 + 8, 0x1000 + 9))))))
|
|
557 << PrintToString(Result);
|
|
558
|
|
559 EXPECT_THAT(Result.ConditionalBranchNodes,
|
|
560 Contains(AllOf(
|
|
561 Field(&ConditionalBranchNode::CFIProtection, Eq(true)),
|
|
562 Field(&ConditionalBranchNode::Address, Eq(0x1000u + 4)),
|
|
563 Field(&ConditionalBranchNode::Target,
|
|
564 HasPath(Result, ElementsAre(0x1000 + 22))),
|
|
565 Field(&ConditionalBranchNode::Fallthrough,
|
|
566 HasPath(Result, ElementsAre(0x1000 + 6, 0x1000 + 7,
|
|
567 0x1000 + 8, 0x1000 + 9))))))
|
|
568 << PrintToString(Result);
|
|
569
|
|
570 EXPECT_THAT(
|
|
571 Result.ConditionalBranchNodes,
|
|
572 Contains(AllOf(Field(&ConditionalBranchNode::CFIProtection, Eq(false)),
|
|
573 Field(&ConditionalBranchNode::Address, Eq(0x1000u + 13)),
|
|
574 Field(&ConditionalBranchNode::Target,
|
|
575 HasPath(Result, ElementsAre(0x1000 + 9))),
|
|
576 Field(&ConditionalBranchNode::Fallthrough,
|
|
577 HasPath(Result, ElementsAre(0x1000 + 15))))))
|
|
578 << PrintToString(Result);
|
|
579
|
|
580 SearchLengthForUndef = PrevSearchLengthForUndef;
|
|
581 }
|
|
582
|
|
583 } // anonymous namespace
|
|
584 } // end namespace cfi_verify
|
|
585 } // end namespace llvm
|