blob: a57958d60e68b89e68345c4bb5cdb70e72ce7928 [file] [log] [blame]
Mitch Phillips99fa1402017-10-23 20:25:19 +00001//===- llvm/unittests/llvm-cfi-verify/GraphBuilder.cpp --------------===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Mitch Phillips99fa1402017-10-23 20:25:19 +00006//
7//===----------------------------------------------------------------------===//
8
9#include "../tools/llvm-cfi-verify/lib/GraphBuilder.h"
10#include "../tools/llvm-cfi-verify/lib/FileAnalysis.h"
11#include "gmock/gmock.h"
12#include "gtest/gtest.h"
13
14#include "llvm/BinaryFormat/ELF.h"
15#include "llvm/MC/MCAsmInfo.h"
16#include "llvm/MC/MCContext.h"
17#include "llvm/MC/MCDisassembler/MCDisassembler.h"
18#include "llvm/MC/MCInst.h"
19#include "llvm/MC/MCInstPrinter.h"
20#include "llvm/MC/MCInstrAnalysis.h"
21#include "llvm/MC/MCInstrDesc.h"
22#include "llvm/MC/MCInstrInfo.h"
23#include "llvm/MC/MCObjectFileInfo.h"
24#include "llvm/MC/MCRegisterInfo.h"
25#include "llvm/MC/MCSubtargetInfo.h"
26#include "llvm/Object/Binary.h"
27#include "llvm/Object/COFF.h"
28#include "llvm/Object/ELFObjectFile.h"
29#include "llvm/Object/ObjectFile.h"
30#include "llvm/Support/Casting.h"
31#include "llvm/Support/CommandLine.h"
32#include "llvm/Support/Error.h"
33#include "llvm/Support/MemoryBuffer.h"
34#include "llvm/Support/TargetRegistry.h"
35#include "llvm/Support/TargetSelect.h"
36#include "llvm/Support/raw_ostream.h"
37
38#include <cstdlib>
39#include <sstream>
40
41using Instr = ::llvm::cfi_verify::FileAnalysis::Instr;
42using ::testing::AllOf;
43using ::testing::Each;
44using ::testing::ElementsAre;
45using ::testing::Eq;
46using ::testing::Field;
47using ::testing::IsEmpty;
48using ::testing::Matches;
49using ::testing::Pair;
50using ::testing::PrintToString;
51using ::testing::Property;
52using ::testing::SizeIs;
53using ::testing::UnorderedElementsAre;
54using ::testing::Value;
55
56namespace llvm {
57namespace cfi_verify {
58// Printing helpers for gtest.
59std::string HexStringifyContainer(const std::vector<uint64_t> &C) {
60 std::stringstream Stream;
61 if (C.empty()) {
62 return "{ }";
63 }
64
65 Stream << "{ ";
66 const auto &LastElemIt = std::end(C) - 1;
67
68 for (auto It = std::begin(C); It != LastElemIt; ++It) {
69 Stream << "0x" << std::hex << *It << ", ";
70 }
71 Stream << "0x" << std::hex << *LastElemIt << " }";
72 return Stream.str();
73}
74
75void PrintTo(const ConditionalBranchNode &BranchNode, ::std::ostream *os) {
76 *os << "ConditionalBranchNode<Address: 0x" << std::hex << BranchNode.Address
77 << ", Target: 0x" << BranchNode.Target << ", Fallthrough: 0x"
78 << BranchNode.Fallthrough
79 << ", CFIProtection: " << BranchNode.CFIProtection << ">";
80}
81
82void PrintTo(const GraphResult &Result, ::std::ostream *os) {
83 *os << "Result BaseAddress: 0x" << std::hex << Result.BaseAddress << "\n";
84
85 if (Result.ConditionalBranchNodes.empty())
86 *os << " (No conditional branch nodes)\n";
87
88 for (const auto &Node : Result.ConditionalBranchNodes) {
89 *os << " ";
90 PrintTo(Node, os);
91 *os << "\n Fallthrough Path: " << std::hex
92 << HexStringifyContainer(Result.flattenAddress(Node.Fallthrough))
93 << "\n";
94 *os << " Target Path: " << std::hex
95 << HexStringifyContainer(Result.flattenAddress(Node.Target)) << "\n";
96 }
97
98 if (Result.OrphanedNodes.empty())
99 *os << " (No orphaned nodes)";
100
101 for (const auto &Orphan : Result.OrphanedNodes) {
102 *os << " Orphan (0x" << std::hex << Orphan
103 << ") Path: " << HexStringifyContainer(Result.flattenAddress(Orphan))
104 << "\n";
105 }
106}
107
108namespace {
109class ELFx86TestFileAnalysis : public FileAnalysis {
110public:
111 ELFx86TestFileAnalysis()
112 : FileAnalysis(Triple("x86_64--"), SubtargetFeatures()) {}
113
114 // Expose this method publicly for testing.
115 void parseSectionContents(ArrayRef<uint8_t> SectionBytes,
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000116 object::SectionedAddress Address) {
117 FileAnalysis::parseSectionContents(SectionBytes, Address);
Mitch Phillips99fa1402017-10-23 20:25:19 +0000118 }
119
120 Error initialiseDisassemblyMembers() {
121 return FileAnalysis::initialiseDisassemblyMembers();
122 }
123};
124
125class BasicGraphBuilderTest : public ::testing::Test {
126protected:
127 virtual void SetUp() {
Mitch Phillipsc15bdf52017-11-03 20:54:26 +0000128 IgnoreDWARFFlag = true;
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000129 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 });
Mitch Phillips99fa1402017-10-23 20:25:19 +0000137 }
138 }
139
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000140 bool SuccessfullyInitialised;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000141 ELFx86TestFileAnalysis Analysis;
142};
143
144MATCHER_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
150TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestSinglePathFallthroughUd2) {
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000151 if (!SuccessfullyInitialised)
152 return;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000153 Analysis.parseSectionContents(
154 {
155 0x75, 0x02, // 0: jne 4 [+2]
156 0x0f, 0x0b, // 2: ud2
157 0xff, 0x10, // 4: callq *(%rax)
158 },
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000159 {0xDEADBEEF, 0x0});
160 const auto Result =
161 GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000162
163 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
164 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));
165 EXPECT_THAT(Result.ConditionalBranchNodes,
166 Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));
167 EXPECT_THAT(
168 Result.ConditionalBranchNodes,
169 Contains(AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
170 Field(&ConditionalBranchNode::Target,
171 HasPath(Result, ElementsAre(0xDEADBEEF + 4))),
172 Field(&ConditionalBranchNode::Fallthrough,
173 HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))
174 << PrintToString(Result);
175}
176
177TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestSinglePathJumpUd2) {
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000178 if (!SuccessfullyInitialised)
179 return;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000180 Analysis.parseSectionContents(
181 {
182 0x75, 0x02, // 0: jne 4 [+2]
183 0xff, 0x10, // 2: callq *(%rax)
184 0x0f, 0x0b, // 4: ud2
185 },
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000186 {0xDEADBEEF, 0x0});
187 const auto Result =
188 GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000189
190 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
191 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));
192 EXPECT_THAT(Result.ConditionalBranchNodes,
193 Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));
194 EXPECT_THAT(
195 Result.ConditionalBranchNodes,
196 Contains(AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
197 Field(&ConditionalBranchNode::Target,
198 HasPath(Result, ElementsAre(0xDEADBEEF + 4))),
199 Field(&ConditionalBranchNode::Fallthrough,
200 HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))
201 << PrintToString(Result);
202}
203
204TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestDualPathDualUd2) {
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000205 if (!SuccessfullyInitialised)
206 return;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000207 Analysis.parseSectionContents(
208 {
209 0x75, 0x03, // 0: jne 5 [+3]
210 0x90, // 2: nop
211 0xff, 0x10, // 3: callq *(%rax)
212 0x0f, 0x0b, // 5: ud2
213 0x75, 0xf9, // 7: jne 2 [-7]
214 0x0f, 0x0b, // 9: ud2
215 },
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000216 {0xDEADBEEF, 0x0});
217 const auto Result =
218 GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000219
220 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
221 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(2));
222 EXPECT_THAT(Result.ConditionalBranchNodes,
223 Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));
224 EXPECT_THAT(
225 Result.ConditionalBranchNodes,
226 Contains(AllOf(
227 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
228 Field(&ConditionalBranchNode::Fallthrough,
229 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))),
230 Field(&ConditionalBranchNode::Target,
231 HasPath(Result, ElementsAre(0xDEADBEEF + 5))))))
232 << PrintToString(Result);
233 EXPECT_THAT(
234 Result.ConditionalBranchNodes,
235 Contains(AllOf(
236 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF + 7)),
237 Field(&ConditionalBranchNode::Fallthrough,
238 HasPath(Result, ElementsAre(0xDEADBEEF + 9))),
239 Field(&ConditionalBranchNode::Target,
240 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))))))
241 << PrintToString(Result);
242}
243
244TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestDualPathSingleUd2) {
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000245 if (!SuccessfullyInitialised)
246 return;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000247 Analysis.parseSectionContents(
248 {
249 0x75, 0x05, // 0: jne 7 [+5]
250 0x90, // 2: nop
251 0xff, 0x10, // 3: callq *(%rax)
252 0x75, 0xfb, // 5: jne 2 [-5]
253 0x0f, 0x0b, // 7: ud2
254 },
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000255 {0xDEADBEEF, 0x0});
256 GraphResult Result =
257 GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000258
259 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
260 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(2));
261 EXPECT_THAT(Result.ConditionalBranchNodes,
262 Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));
263 EXPECT_THAT(
264 Result.ConditionalBranchNodes,
265 Contains(AllOf(
266 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
267 Field(&ConditionalBranchNode::Fallthrough,
268 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))),
269 Field(&ConditionalBranchNode::Target,
270 HasPath(Result, ElementsAre(0xDEADBEEF + 7))))))
271 << PrintToString(Result);
272 EXPECT_THAT(
273 Result.ConditionalBranchNodes,
274 Contains(AllOf(
275 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF + 5)),
276 Field(&ConditionalBranchNode::Fallthrough,
277 HasPath(Result, ElementsAre(0xDEADBEEF + 7))),
278 Field(&ConditionalBranchNode::Target,
279 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))))))
280 << PrintToString(Result);
281}
282
283TEST_F(BasicGraphBuilderTest, BuildFlowGraphFailures) {
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000284 if (!SuccessfullyInitialised)
285 return;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000286 Analysis.parseSectionContents(
287 {
288 0x90, // 0: nop
289 0x75, 0xfe, // 1: jne 1 [-2]
290 },
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000291 {0xDEADBEEF, 0x0});
292 GraphResult Result =
293 GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000294 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
295 EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
296
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000297 Result = GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 1, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000298 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
299 EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
300
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000301 Result = GraphBuilder::buildFlowGraph(Analysis, {0xDEADC0DE, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000302 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
303 EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
304}
305
306TEST_F(BasicGraphBuilderTest, BuildFlowGraphNoXrefs) {
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000307 if (!SuccessfullyInitialised)
308 return;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000309 Analysis.parseSectionContents(
310 {
311 0xeb, 0xfe, // 0: jmp 0 [-2]
312 0xff, 0x10, // 2: callq *(%rax)
313 },
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000314 {0xDEADBEEF, 0x0});
315 GraphResult Result =
316 GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000317 EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
318 EXPECT_THAT(Result.OrphanedNodes, ElementsAre(0xDEADBEEF + 2));
319 EXPECT_THAT(Result.IntermediateNodes, IsEmpty());
320}
321
322TEST_F(BasicGraphBuilderTest, BuildFlowGraphConditionalInfiniteLoop) {
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000323 if (!SuccessfullyInitialised)
324 return;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000325 Analysis.parseSectionContents(
326 {
327 0x75, 0xfe, // 0: jne 0 [-2]
328 0xff, 0x10, // 2: callq *(%rax)
329 },
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000330 {0xDEADBEEF, 0x0});
331 GraphResult Result =
332 GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000333 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
334 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));
335 EXPECT_THAT(
336 Result.ConditionalBranchNodes,
337 Each(AllOf(Field(&ConditionalBranchNode::CFIProtection, Eq(false)),
338 Field(&ConditionalBranchNode::Target,
339 HasPath(Result, ElementsAre(0xDEADBEEF))),
340 Field(&ConditionalBranchNode::Fallthrough,
341 HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))
342 << PrintToString(Result);
343}
344
345TEST_F(BasicGraphBuilderTest, BuildFlowGraphUnconditionalInfiniteLoop) {
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000346 if (!SuccessfullyInitialised)
347 return;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000348 Analysis.parseSectionContents(
349 {
350 0x75, 0x02, // 0: jne 4 [+2]
351 0xeb, 0xfc, // 2: jmp 0 [-4]
352 0xff, 0x10, // 4: callq *(%rax)
353 },
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000354 {0xDEADBEEF, 0x0});
355 GraphResult Result =
356 GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000357 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
358 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));
359 EXPECT_THAT(
360 Result.ConditionalBranchNodes,
361 Contains(
362 AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
363 Field(&ConditionalBranchNode::Fallthrough,
364 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF))),
365 Field(&ConditionalBranchNode::Target,
366 HasPath(Result, ElementsAre(0xDEADBEEF + 4))))))
367 << PrintToString(Result);
368}
369
370TEST_F(BasicGraphBuilderTest, BuildFlowGraphNoFlowsToIndirection) {
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000371 if (!SuccessfullyInitialised)
372 return;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000373 Analysis.parseSectionContents(
374 {
375 0x75, 0x00, // 0: jne 2 [+0]
376 0xeb, 0xfc, // 2: jmp 0 [-4]
377 0xff, 0x10, // 4: callq *(%rax)
378 },
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000379 {0xDEADBEEF, 0x0});
380 GraphResult Result =
381 GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000382 EXPECT_THAT(Result.OrphanedNodes, ElementsAre(0xDEADBEEF + 4));
383 EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
384}
385
386TEST_F(BasicGraphBuilderTest, BuildFlowGraphLengthExceededUpwards) {
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000387 if (!SuccessfullyInitialised)
388 return;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000389 Analysis.parseSectionContents(
390 {
391 0x75, 0x06, // 0: jne 8 [+6]
392 0x90, // 2: nop
393 0x90, // 3: nop
394 0x90, // 4: nop
395 0x90, // 5: nop
396 0xff, 0x10, // 6: callq *(%rax)
397 0x0f, 0x0b, // 8: ud2
398 },
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000399 {0xDEADBEEF, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000400 uint64_t PrevSearchLengthForConditionalBranch =
401 SearchLengthForConditionalBranch;
402 SearchLengthForConditionalBranch = 2;
403
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000404 GraphResult Result =
405 GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 6, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000406 EXPECT_THAT(Result.OrphanedNodes, SizeIs(1));
407 EXPECT_THAT(Result.OrphanedNodes,
408 Each(HasPath(Result, ElementsAre(0xDEADBEEF + 4, 0xDEADBEEF + 5,
409 0xDEADBEEF + 6))))
410 << PrintToString(Result);
411 EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
412
413 SearchLengthForConditionalBranch = PrevSearchLengthForConditionalBranch;
414}
415
416TEST_F(BasicGraphBuilderTest, BuildFlowGraphLengthExceededDownwards) {
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000417 if (!SuccessfullyInitialised)
418 return;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000419 Analysis.parseSectionContents(
420 {
421 0x75, 0x02, // 0: jne 4 [+2]
422 0xff, 0x10, // 2: callq *(%rax)
423 0x90, // 4: nop
424 0x90, // 5: nop
425 0x90, // 6: nop
426 0x90, // 7: nop
427 0x0f, 0x0b, // 8: ud2
428 },
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000429 {0xDEADBEEF, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000430 uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;
431 SearchLengthForUndef = 2;
432
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000433 GraphResult Result =
434 GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000435 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
436 EXPECT_THAT(
437 Result.ConditionalBranchNodes,
438 Each(AllOf(
439 Field(&ConditionalBranchNode::CFIProtection, Eq(false)),
440 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
441 Field(&ConditionalBranchNode::Target,
442 HasPath(Result, ElementsAre(0xDEADBEEF + 4, 0xDEADBEEF + 5))),
443 Field(&ConditionalBranchNode::Fallthrough,
444 HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))
445 << PrintToString(Result);
446
447 SearchLengthForUndef = PrevSearchLengthForUndef;
448}
449
450// This test ensures when avoiding doing repeated work we still generate the
451// paths correctly. We don't need to recalculate the flow from 0x2 -> 0x3 as it
452// should only need to be generated once.
453TEST_F(BasicGraphBuilderTest, BuildFlowGraphWithRepeatedWork) {
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000454 if (!SuccessfullyInitialised)
455 return;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000456 Analysis.parseSectionContents(
457 {
458 0x75, 0x05, // 0: jne 7 [+5]
459 0x90, // 2: nop
460 0xff, 0x10, // 3: callq *(%rax)
461 0x75, 0xfb, // 5: jne 2 [-5]
462 0x0f, 0x0b, // 7: ud2
463 },
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000464 {0xDEADBEEF, 0x0});
465 GraphResult Result =
466 GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000467 EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
468 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(2));
469 EXPECT_THAT(
470 Result.ConditionalBranchNodes,
471 Contains(AllOf(
472 Field(&ConditionalBranchNode::CFIProtection, Eq(true)),
473 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
474 Field(&ConditionalBranchNode::Target,
475 HasPath(Result, ElementsAre(0xDEADBEEF + 7))),
476 Field(&ConditionalBranchNode::Fallthrough,
477 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))))))
478 << PrintToString(Result);
479 EXPECT_THAT(
480 Result.ConditionalBranchNodes,
481 Contains(AllOf(
482 Field(&ConditionalBranchNode::CFIProtection, Eq(true)),
483 Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF + 5)),
484 Field(&ConditionalBranchNode::Target,
485 HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))),
486 Field(&ConditionalBranchNode::Fallthrough,
487 HasPath(Result, ElementsAre(0xDEADBEEF + 7))))))
488 << PrintToString(Result);
489 EXPECT_THAT(Result.IntermediateNodes, SizeIs(1));
490 EXPECT_THAT(Result.IntermediateNodes,
491 UnorderedElementsAre(Pair(0xDEADBEEF + 2, 0xDEADBEEF + 3)));
492}
493
494TEST_F(BasicGraphBuilderTest, BuildFlowGraphComplexExample) {
Mitch Phillipsd9af3832017-10-23 20:54:01 +0000495 if (!SuccessfullyInitialised)
496 return;
Mitch Phillips99fa1402017-10-23 20:25:19 +0000497 // The following code has this graph:
498 // +----------+ +--------------+
499 // | 20 | <--- | 0 |
500 // +----------+ +--------------+
501 // | |
502 // v v
503 // +----------+ +--------------+
504 // | 21 | | 2 |
505 // +----------+ +--------------+
506 // | |
507 // v v
508 // +----------+ +--------------+
509 // | 22 (ud2) | +-> | 7 |
510 // +----------+ | +--------------+
511 // ^ | |
512 // | | v
513 // +----------+ | +--------------+
514 // | 4 | | | 8 |
515 // +----------+ | +--------------+
516 // | | |
517 // v | v
518 // +----------+ | +--------------+ +------------+
519 // | 6 | -+ | 9 (indirect) | <- | 13 |
520 // +----------+ +--------------+ +------------+
521 // ^ |
522 // | v
523 // +--------------+ +------------+
524 // | 11 | | 15 (error) |
525 // +--------------+ +------------+
526 // Or, in image format: https://i.imgur.com/aX5fCoi.png
527
528 Analysis.parseSectionContents(
529 {
530 0x75, 0x12, // 0: jne 20 [+18]
531 0xeb, 0x03, // 2: jmp 7 [+3]
532 0x75, 0x10, // 4: jne 22 [+16]
533 0x90, // 6: nop
534 0x90, // 7: nop
535 0x90, // 8: nop
536 0xff, 0x10, // 9: callq *(%rax)
537 0xeb, 0xfc, // 11: jmp 9 [-4]
538 0x75, 0xfa, // 13: jne 9 [-6]
539 0xe8, 0x78, 0x56, 0x34, 0x12, // 15: callq OUTOFBOUNDS [+0x12345678]
540 0x90, // 20: nop
541 0x90, // 21: nop
542 0x0f, 0x0b, // 22: ud2
543 },
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000544 {0x1000, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000545 uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;
546 SearchLengthForUndef = 5;
547
Alexey Lapshin77fc1f62019-02-27 13:17:36 +0000548 GraphResult Result =
549 GraphBuilder::buildFlowGraph(Analysis, {0x1000 + 9, 0x0});
Mitch Phillips99fa1402017-10-23 20:25:19 +0000550
551 EXPECT_THAT(Result.OrphanedNodes, SizeIs(1));
552 EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(3));
553
554 EXPECT_THAT(
555 Result.OrphanedNodes,
556 Each(AllOf(Eq(0x1000u + 11),
557 HasPath(Result, ElementsAre(0x1000 + 11, 0x1000 + 9)))))
558 << PrintToString(Result);
559
560 EXPECT_THAT(Result.ConditionalBranchNodes,
561 Contains(AllOf(
562 Field(&ConditionalBranchNode::CFIProtection, Eq(true)),
563 Field(&ConditionalBranchNode::Address, Eq(0x1000u)),
564 Field(&ConditionalBranchNode::Target,
565 HasPath(Result, ElementsAre(0x1000 + 20, 0x1000 + 21,
566 0x1000 + 22))),
567 Field(&ConditionalBranchNode::Fallthrough,
568 HasPath(Result, ElementsAre(0x1000 + 2, 0x1000 + 7,
569 0x1000 + 8, 0x1000 + 9))))))
570 << PrintToString(Result);
571
572 EXPECT_THAT(Result.ConditionalBranchNodes,
573 Contains(AllOf(
574 Field(&ConditionalBranchNode::CFIProtection, Eq(true)),
575 Field(&ConditionalBranchNode::Address, Eq(0x1000u + 4)),
576 Field(&ConditionalBranchNode::Target,
577 HasPath(Result, ElementsAre(0x1000 + 22))),
578 Field(&ConditionalBranchNode::Fallthrough,
579 HasPath(Result, ElementsAre(0x1000 + 6, 0x1000 + 7,
580 0x1000 + 8, 0x1000 + 9))))))
581 << PrintToString(Result);
582
583 EXPECT_THAT(
584 Result.ConditionalBranchNodes,
585 Contains(AllOf(Field(&ConditionalBranchNode::CFIProtection, Eq(false)),
586 Field(&ConditionalBranchNode::Address, Eq(0x1000u + 13)),
587 Field(&ConditionalBranchNode::Target,
588 HasPath(Result, ElementsAre(0x1000 + 9))),
589 Field(&ConditionalBranchNode::Fallthrough,
590 HasPath(Result, ElementsAre(0x1000 + 15))))))
591 << PrintToString(Result);
592
593 SearchLengthForUndef = PrevSearchLengthForUndef;
594}
595
596} // anonymous namespace
597} // end namespace cfi_verify
598} // end namespace llvm