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