199fa1405SMitch Phillips //===- llvm/unittests/llvm-cfi-verify/GraphBuilder.cpp --------------===//
299fa1405SMitch Phillips //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
699fa1405SMitch Phillips //
799fa1405SMitch Phillips //===----------------------------------------------------------------------===//
899fa1405SMitch Phillips
999fa1405SMitch Phillips #include "../tools/llvm-cfi-verify/lib/GraphBuilder.h"
1099fa1405SMitch Phillips #include "../tools/llvm-cfi-verify/lib/FileAnalysis.h"
1199fa1405SMitch Phillips #include "gmock/gmock.h"
1299fa1405SMitch Phillips #include "gtest/gtest.h"
1399fa1405SMitch Phillips
1499fa1405SMitch Phillips #include "llvm/BinaryFormat/ELF.h"
15db29f437Sserge-sans-paille #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
1699fa1405SMitch Phillips #include "llvm/MC/MCAsmInfo.h"
1799fa1405SMitch Phillips #include "llvm/MC/MCContext.h"
1899fa1405SMitch Phillips #include "llvm/MC/MCDisassembler/MCDisassembler.h"
1999fa1405SMitch Phillips #include "llvm/MC/MCInst.h"
2099fa1405SMitch Phillips #include "llvm/MC/MCInstPrinter.h"
2199fa1405SMitch Phillips #include "llvm/MC/MCInstrAnalysis.h"
2299fa1405SMitch Phillips #include "llvm/MC/MCInstrDesc.h"
2399fa1405SMitch Phillips #include "llvm/MC/MCInstrInfo.h"
2499fa1405SMitch Phillips #include "llvm/MC/MCObjectFileInfo.h"
2599fa1405SMitch Phillips #include "llvm/MC/MCRegisterInfo.h"
2699fa1405SMitch Phillips #include "llvm/MC/MCSubtargetInfo.h"
2789b57061SReid Kleckner #include "llvm/MC/TargetRegistry.h"
2899fa1405SMitch Phillips #include "llvm/Object/Binary.h"
2999fa1405SMitch Phillips #include "llvm/Object/COFF.h"
3099fa1405SMitch Phillips #include "llvm/Object/ELFObjectFile.h"
3199fa1405SMitch Phillips #include "llvm/Object/ObjectFile.h"
3299fa1405SMitch Phillips #include "llvm/Support/Casting.h"
3399fa1405SMitch Phillips #include "llvm/Support/CommandLine.h"
3499fa1405SMitch Phillips #include "llvm/Support/Error.h"
3599fa1405SMitch Phillips #include "llvm/Support/MemoryBuffer.h"
3699fa1405SMitch Phillips #include "llvm/Support/TargetSelect.h"
3799fa1405SMitch Phillips #include "llvm/Support/raw_ostream.h"
3899fa1405SMitch Phillips
3999fa1405SMitch Phillips #include <cstdlib>
4099fa1405SMitch Phillips #include <sstream>
4199fa1405SMitch Phillips
4299fa1405SMitch Phillips using Instr = ::llvm::cfi_verify::FileAnalysis::Instr;
4399fa1405SMitch Phillips using ::testing::AllOf;
4499fa1405SMitch Phillips using ::testing::Each;
4599fa1405SMitch Phillips using ::testing::ElementsAre;
4699fa1405SMitch Phillips using ::testing::Eq;
4799fa1405SMitch Phillips using ::testing::Field;
4899fa1405SMitch Phillips using ::testing::IsEmpty;
4999fa1405SMitch Phillips using ::testing::Matches;
5099fa1405SMitch Phillips using ::testing::Pair;
5199fa1405SMitch Phillips using ::testing::PrintToString;
5299fa1405SMitch Phillips using ::testing::SizeIs;
5399fa1405SMitch Phillips using ::testing::UnorderedElementsAre;
5499fa1405SMitch Phillips
5599fa1405SMitch Phillips namespace llvm {
5699fa1405SMitch Phillips namespace cfi_verify {
5799fa1405SMitch Phillips // Printing helpers for gtest.
HexStringifyContainer(const std::vector<uint64_t> & C)5899fa1405SMitch Phillips std::string HexStringifyContainer(const std::vector<uint64_t> &C) {
5999fa1405SMitch Phillips std::stringstream Stream;
6099fa1405SMitch Phillips if (C.empty()) {
6199fa1405SMitch Phillips return "{ }";
6299fa1405SMitch Phillips }
6399fa1405SMitch Phillips
6499fa1405SMitch Phillips Stream << "{ ";
6599fa1405SMitch Phillips const auto &LastElemIt = std::end(C) - 1;
6699fa1405SMitch Phillips
6799fa1405SMitch Phillips for (auto It = std::begin(C); It != LastElemIt; ++It) {
6899fa1405SMitch Phillips Stream << "0x" << std::hex << *It << ", ";
6999fa1405SMitch Phillips }
7099fa1405SMitch Phillips Stream << "0x" << std::hex << *LastElemIt << " }";
7199fa1405SMitch Phillips return Stream.str();
7299fa1405SMitch Phillips }
7399fa1405SMitch Phillips
PrintTo(const ConditionalBranchNode & BranchNode,::std::ostream * os)7499fa1405SMitch Phillips void PrintTo(const ConditionalBranchNode &BranchNode, ::std::ostream *os) {
7599fa1405SMitch Phillips *os << "ConditionalBranchNode<Address: 0x" << std::hex << BranchNode.Address
7699fa1405SMitch Phillips << ", Target: 0x" << BranchNode.Target << ", Fallthrough: 0x"
7799fa1405SMitch Phillips << BranchNode.Fallthrough
7899fa1405SMitch Phillips << ", CFIProtection: " << BranchNode.CFIProtection << ">";
7999fa1405SMitch Phillips }
8099fa1405SMitch Phillips
PrintTo(const GraphResult & Result,::std::ostream * os)8199fa1405SMitch Phillips void PrintTo(const GraphResult &Result, ::std::ostream *os) {
8299fa1405SMitch Phillips *os << "Result BaseAddress: 0x" << std::hex << Result.BaseAddress << "\n";
8399fa1405SMitch Phillips
8499fa1405SMitch Phillips if (Result.ConditionalBranchNodes.empty())
8599fa1405SMitch Phillips *os << " (No conditional branch nodes)\n";
8699fa1405SMitch Phillips
8799fa1405SMitch Phillips for (const auto &Node : Result.ConditionalBranchNodes) {
8899fa1405SMitch Phillips *os << " ";
8999fa1405SMitch Phillips PrintTo(Node, os);
9099fa1405SMitch Phillips *os << "\n Fallthrough Path: " << std::hex
9199fa1405SMitch Phillips << HexStringifyContainer(Result.flattenAddress(Node.Fallthrough))
9299fa1405SMitch Phillips << "\n";
9399fa1405SMitch Phillips *os << " Target Path: " << std::hex
9499fa1405SMitch Phillips << HexStringifyContainer(Result.flattenAddress(Node.Target)) << "\n";
9599fa1405SMitch Phillips }
9699fa1405SMitch Phillips
9799fa1405SMitch Phillips if (Result.OrphanedNodes.empty())
9899fa1405SMitch Phillips *os << " (No orphaned nodes)";
9999fa1405SMitch Phillips
10099fa1405SMitch Phillips for (const auto &Orphan : Result.OrphanedNodes) {
10199fa1405SMitch Phillips *os << " Orphan (0x" << std::hex << Orphan
10299fa1405SMitch Phillips << ") Path: " << HexStringifyContainer(Result.flattenAddress(Orphan))
10399fa1405SMitch Phillips << "\n";
10499fa1405SMitch Phillips }
10599fa1405SMitch Phillips }
10699fa1405SMitch Phillips
10799fa1405SMitch Phillips namespace {
10899fa1405SMitch Phillips class ELFx86TestFileAnalysis : public FileAnalysis {
10999fa1405SMitch Phillips public:
ELFx86TestFileAnalysis()11099fa1405SMitch Phillips ELFx86TestFileAnalysis()
11199fa1405SMitch Phillips : FileAnalysis(Triple("x86_64--"), SubtargetFeatures()) {}
11299fa1405SMitch Phillips
11399fa1405SMitch Phillips // Expose this method publicly for testing.
parseSectionContents(ArrayRef<uint8_t> SectionBytes,object::SectionedAddress Address)11499fa1405SMitch Phillips void parseSectionContents(ArrayRef<uint8_t> SectionBytes,
11577fc1f60SAlexey Lapshin object::SectionedAddress Address) {
11677fc1f60SAlexey Lapshin FileAnalysis::parseSectionContents(SectionBytes, Address);
11799fa1405SMitch Phillips }
11899fa1405SMitch Phillips
initialiseDisassemblyMembers()11999fa1405SMitch Phillips Error initialiseDisassemblyMembers() {
12099fa1405SMitch Phillips return FileAnalysis::initialiseDisassemblyMembers();
12199fa1405SMitch Phillips }
12299fa1405SMitch Phillips };
12399fa1405SMitch Phillips
12499fa1405SMitch Phillips class BasicGraphBuilderTest : public ::testing::Test {
12599fa1405SMitch Phillips protected:
SetUp()12631eb8349SLogan Smith void SetUp() override {
127c15bdf55SMitch Phillips IgnoreDWARFFlag = true;
128d9af383dSMitch Phillips SuccessfullyInitialised = true;
129d9af383dSMitch Phillips if (auto Err = Analysis.initialiseDisassemblyMembers()) {
130d9af383dSMitch Phillips handleAllErrors(std::move(Err), [&](const UnsupportedDisassembly &E) {
131d9af383dSMitch Phillips SuccessfullyInitialised = false;
132d9af383dSMitch Phillips outs()
133d9af383dSMitch Phillips << "Note: CFIVerifyTests are disabled due to lack of x86 support "
134d9af383dSMitch Phillips "on this build.\n";
135d9af383dSMitch Phillips });
13699fa1405SMitch Phillips }
13799fa1405SMitch Phillips }
13899fa1405SMitch Phillips
139d9af383dSMitch Phillips bool SuccessfullyInitialised;
14099fa1405SMitch Phillips ELFx86TestFileAnalysis Analysis;
14199fa1405SMitch Phillips };
14299fa1405SMitch Phillips
14399fa1405SMitch Phillips MATCHER_P2(HasPath, Result, Matcher, "has path " + PrintToString(Matcher)) {
14499fa1405SMitch Phillips const auto &Path = Result.flattenAddress(arg);
14599fa1405SMitch Phillips *result_listener << "the path is " << PrintToString(Path);
14699fa1405SMitch Phillips return Matches(Matcher)(Path);
14799fa1405SMitch Phillips }
14899fa1405SMitch Phillips
TEST_F(BasicGraphBuilderTest,BuildFlowGraphTestSinglePathFallthroughUd2)14999fa1405SMitch Phillips TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestSinglePathFallthroughUd2) {
150d9af383dSMitch Phillips if (!SuccessfullyInitialised)
151*7fc87159SPaul Robinson GTEST_SKIP();
15299fa1405SMitch Phillips Analysis.parseSectionContents(
15399fa1405SMitch Phillips {
15499fa1405SMitch Phillips 0x75, 0x02, // 0: jne 4 [+2]
15599fa1405SMitch Phillips 0x0f, 0x0b, // 2: ud2
15699fa1405SMitch Phillips 0xff, 0x10, // 4: callq *(%rax)
15799fa1405SMitch Phillips },
15877fc1f60SAlexey Lapshin {0xDEADBEEF, 0x0});
15977fc1f60SAlexey Lapshin const auto Result =
16077fc1f60SAlexey Lapshin GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});
16199fa1405SMitch Phillips
16299fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
16399fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));
16499fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes,
16599fa1405SMitch Phillips Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));
16699fa1405SMitch Phillips EXPECT_THAT(
16799fa1405SMitch Phillips Result.ConditionalBranchNodes,
16899fa1405SMitch Phillips Contains(AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
16999fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
17099fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 4))),
17199fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
17299fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))
17399fa1405SMitch Phillips << PrintToString(Result);
17499fa1405SMitch Phillips }
17599fa1405SMitch Phillips
TEST_F(BasicGraphBuilderTest,BuildFlowGraphTestSinglePathJumpUd2)17699fa1405SMitch Phillips TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestSinglePathJumpUd2) {
177d9af383dSMitch Phillips if (!SuccessfullyInitialised)
178*7fc87159SPaul Robinson GTEST_SKIP();
17999fa1405SMitch Phillips Analysis.parseSectionContents(
18099fa1405SMitch Phillips {
18199fa1405SMitch Phillips 0x75, 0x02, // 0: jne 4 [+2]
18299fa1405SMitch Phillips 0xff, 0x10, // 2: callq *(%rax)
18399fa1405SMitch Phillips 0x0f, 0x0b, // 4: ud2
18499fa1405SMitch Phillips },
18577fc1f60SAlexey Lapshin {0xDEADBEEF, 0x0});
18677fc1f60SAlexey Lapshin const auto Result =
18777fc1f60SAlexey Lapshin GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});
18899fa1405SMitch Phillips
18999fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
19099fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));
19199fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes,
19299fa1405SMitch Phillips Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));
19399fa1405SMitch Phillips EXPECT_THAT(
19499fa1405SMitch Phillips Result.ConditionalBranchNodes,
19599fa1405SMitch Phillips Contains(AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
19699fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
19799fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 4))),
19899fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
19999fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))
20099fa1405SMitch Phillips << PrintToString(Result);
20199fa1405SMitch Phillips }
20299fa1405SMitch Phillips
TEST_F(BasicGraphBuilderTest,BuildFlowGraphTestDualPathDualUd2)20399fa1405SMitch Phillips TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestDualPathDualUd2) {
204d9af383dSMitch Phillips if (!SuccessfullyInitialised)
205*7fc87159SPaul Robinson GTEST_SKIP();
20699fa1405SMitch Phillips Analysis.parseSectionContents(
20799fa1405SMitch Phillips {
20899fa1405SMitch Phillips 0x75, 0x03, // 0: jne 5 [+3]
20999fa1405SMitch Phillips 0x90, // 2: nop
21099fa1405SMitch Phillips 0xff, 0x10, // 3: callq *(%rax)
21199fa1405SMitch Phillips 0x0f, 0x0b, // 5: ud2
21299fa1405SMitch Phillips 0x75, 0xf9, // 7: jne 2 [-7]
21399fa1405SMitch Phillips 0x0f, 0x0b, // 9: ud2
21499fa1405SMitch Phillips },
21577fc1f60SAlexey Lapshin {0xDEADBEEF, 0x0});
21677fc1f60SAlexey Lapshin const auto Result =
21777fc1f60SAlexey Lapshin GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});
21899fa1405SMitch Phillips
21999fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
22099fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(2));
22199fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes,
22299fa1405SMitch Phillips Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));
22399fa1405SMitch Phillips EXPECT_THAT(
22499fa1405SMitch Phillips Result.ConditionalBranchNodes,
22599fa1405SMitch Phillips Contains(AllOf(
22699fa1405SMitch Phillips Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
22799fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
22899fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))),
22999fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
23099fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 5))))))
23199fa1405SMitch Phillips << PrintToString(Result);
23299fa1405SMitch Phillips EXPECT_THAT(
23399fa1405SMitch Phillips Result.ConditionalBranchNodes,
23499fa1405SMitch Phillips Contains(AllOf(
23599fa1405SMitch Phillips Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF + 7)),
23699fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
23799fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 9))),
23899fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
23999fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))))))
24099fa1405SMitch Phillips << PrintToString(Result);
24199fa1405SMitch Phillips }
24299fa1405SMitch Phillips
TEST_F(BasicGraphBuilderTest,BuildFlowGraphTestDualPathSingleUd2)24399fa1405SMitch Phillips TEST_F(BasicGraphBuilderTest, BuildFlowGraphTestDualPathSingleUd2) {
244d9af383dSMitch Phillips if (!SuccessfullyInitialised)
245*7fc87159SPaul Robinson GTEST_SKIP();
24699fa1405SMitch Phillips Analysis.parseSectionContents(
24799fa1405SMitch Phillips {
24899fa1405SMitch Phillips 0x75, 0x05, // 0: jne 7 [+5]
24999fa1405SMitch Phillips 0x90, // 2: nop
25099fa1405SMitch Phillips 0xff, 0x10, // 3: callq *(%rax)
25199fa1405SMitch Phillips 0x75, 0xfb, // 5: jne 2 [-5]
25299fa1405SMitch Phillips 0x0f, 0x0b, // 7: ud2
25399fa1405SMitch Phillips },
25477fc1f60SAlexey Lapshin {0xDEADBEEF, 0x0});
25577fc1f60SAlexey Lapshin GraphResult Result =
25677fc1f60SAlexey Lapshin GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});
25799fa1405SMitch Phillips
25899fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
25999fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(2));
26099fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes,
26199fa1405SMitch Phillips Each(Field(&ConditionalBranchNode::CFIProtection, Eq(true))));
26299fa1405SMitch Phillips EXPECT_THAT(
26399fa1405SMitch Phillips Result.ConditionalBranchNodes,
26499fa1405SMitch Phillips Contains(AllOf(
26599fa1405SMitch Phillips Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
26699fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
26799fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))),
26899fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
26999fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 7))))))
27099fa1405SMitch Phillips << PrintToString(Result);
27199fa1405SMitch Phillips EXPECT_THAT(
27299fa1405SMitch Phillips Result.ConditionalBranchNodes,
27399fa1405SMitch Phillips Contains(AllOf(
27499fa1405SMitch Phillips Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF + 5)),
27599fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
27699fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 7))),
27799fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
27899fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))))))
27999fa1405SMitch Phillips << PrintToString(Result);
28099fa1405SMitch Phillips }
28199fa1405SMitch Phillips
TEST_F(BasicGraphBuilderTest,BuildFlowGraphFailures)28299fa1405SMitch Phillips TEST_F(BasicGraphBuilderTest, BuildFlowGraphFailures) {
283d9af383dSMitch Phillips if (!SuccessfullyInitialised)
284*7fc87159SPaul Robinson GTEST_SKIP();
28599fa1405SMitch Phillips Analysis.parseSectionContents(
28699fa1405SMitch Phillips {
28799fa1405SMitch Phillips 0x90, // 0: nop
28899fa1405SMitch Phillips 0x75, 0xfe, // 1: jne 1 [-2]
28999fa1405SMitch Phillips },
29077fc1f60SAlexey Lapshin {0xDEADBEEF, 0x0});
29177fc1f60SAlexey Lapshin GraphResult Result =
29277fc1f60SAlexey Lapshin GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF, 0x0});
29399fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
29499fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
29599fa1405SMitch Phillips
29677fc1f60SAlexey Lapshin Result = GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 1, 0x0});
29799fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
29899fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
29999fa1405SMitch Phillips
30077fc1f60SAlexey Lapshin Result = GraphBuilder::buildFlowGraph(Analysis, {0xDEADC0DE, 0x0});
30199fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
30299fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
30399fa1405SMitch Phillips }
30499fa1405SMitch Phillips
TEST_F(BasicGraphBuilderTest,BuildFlowGraphNoXrefs)30599fa1405SMitch Phillips TEST_F(BasicGraphBuilderTest, BuildFlowGraphNoXrefs) {
306d9af383dSMitch Phillips if (!SuccessfullyInitialised)
307*7fc87159SPaul Robinson GTEST_SKIP();
30899fa1405SMitch Phillips Analysis.parseSectionContents(
30999fa1405SMitch Phillips {
31099fa1405SMitch Phillips 0xeb, 0xfe, // 0: jmp 0 [-2]
31199fa1405SMitch Phillips 0xff, 0x10, // 2: callq *(%rax)
31299fa1405SMitch Phillips },
31377fc1f60SAlexey Lapshin {0xDEADBEEF, 0x0});
31477fc1f60SAlexey Lapshin GraphResult Result =
31577fc1f60SAlexey Lapshin GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});
31699fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
31799fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, ElementsAre(0xDEADBEEF + 2));
31899fa1405SMitch Phillips EXPECT_THAT(Result.IntermediateNodes, IsEmpty());
31999fa1405SMitch Phillips }
32099fa1405SMitch Phillips
TEST_F(BasicGraphBuilderTest,BuildFlowGraphConditionalInfiniteLoop)32199fa1405SMitch Phillips TEST_F(BasicGraphBuilderTest, BuildFlowGraphConditionalInfiniteLoop) {
322d9af383dSMitch Phillips if (!SuccessfullyInitialised)
323*7fc87159SPaul Robinson GTEST_SKIP();
32499fa1405SMitch Phillips Analysis.parseSectionContents(
32599fa1405SMitch Phillips {
32699fa1405SMitch Phillips 0x75, 0xfe, // 0: jne 0 [-2]
32799fa1405SMitch Phillips 0xff, 0x10, // 2: callq *(%rax)
32899fa1405SMitch Phillips },
32977fc1f60SAlexey Lapshin {0xDEADBEEF, 0x0});
33077fc1f60SAlexey Lapshin GraphResult Result =
33177fc1f60SAlexey Lapshin GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});
33299fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
33399fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));
33499fa1405SMitch Phillips EXPECT_THAT(
33599fa1405SMitch Phillips Result.ConditionalBranchNodes,
33699fa1405SMitch Phillips Each(AllOf(Field(&ConditionalBranchNode::CFIProtection, Eq(false)),
33799fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
33899fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF))),
33999fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
34099fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))
34199fa1405SMitch Phillips << PrintToString(Result);
34299fa1405SMitch Phillips }
34399fa1405SMitch Phillips
TEST_F(BasicGraphBuilderTest,BuildFlowGraphUnconditionalInfiniteLoop)34499fa1405SMitch Phillips TEST_F(BasicGraphBuilderTest, BuildFlowGraphUnconditionalInfiniteLoop) {
345d9af383dSMitch Phillips if (!SuccessfullyInitialised)
346*7fc87159SPaul Robinson GTEST_SKIP();
34799fa1405SMitch Phillips Analysis.parseSectionContents(
34899fa1405SMitch Phillips {
34999fa1405SMitch Phillips 0x75, 0x02, // 0: jne 4 [+2]
35099fa1405SMitch Phillips 0xeb, 0xfc, // 2: jmp 0 [-4]
35199fa1405SMitch Phillips 0xff, 0x10, // 4: callq *(%rax)
35299fa1405SMitch Phillips },
35377fc1f60SAlexey Lapshin {0xDEADBEEF, 0x0});
35477fc1f60SAlexey Lapshin GraphResult Result =
35577fc1f60SAlexey Lapshin GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});
35699fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
35799fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(1));
35899fa1405SMitch Phillips EXPECT_THAT(
35999fa1405SMitch Phillips Result.ConditionalBranchNodes,
36099fa1405SMitch Phillips Contains(
36199fa1405SMitch Phillips AllOf(Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
36299fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
36399fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF))),
36499fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
36599fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 4))))))
36699fa1405SMitch Phillips << PrintToString(Result);
36799fa1405SMitch Phillips }
36899fa1405SMitch Phillips
TEST_F(BasicGraphBuilderTest,BuildFlowGraphNoFlowsToIndirection)36999fa1405SMitch Phillips TEST_F(BasicGraphBuilderTest, BuildFlowGraphNoFlowsToIndirection) {
370d9af383dSMitch Phillips if (!SuccessfullyInitialised)
371*7fc87159SPaul Robinson GTEST_SKIP();
37299fa1405SMitch Phillips Analysis.parseSectionContents(
37399fa1405SMitch Phillips {
37499fa1405SMitch Phillips 0x75, 0x00, // 0: jne 2 [+0]
37599fa1405SMitch Phillips 0xeb, 0xfc, // 2: jmp 0 [-4]
37699fa1405SMitch Phillips 0xff, 0x10, // 4: callq *(%rax)
37799fa1405SMitch Phillips },
37877fc1f60SAlexey Lapshin {0xDEADBEEF, 0x0});
37977fc1f60SAlexey Lapshin GraphResult Result =
38077fc1f60SAlexey Lapshin GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});
38199fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, ElementsAre(0xDEADBEEF + 4));
38299fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
38399fa1405SMitch Phillips }
38499fa1405SMitch Phillips
TEST_F(BasicGraphBuilderTest,BuildFlowGraphLengthExceededUpwards)38599fa1405SMitch Phillips TEST_F(BasicGraphBuilderTest, BuildFlowGraphLengthExceededUpwards) {
386d9af383dSMitch Phillips if (!SuccessfullyInitialised)
387*7fc87159SPaul Robinson GTEST_SKIP();
38899fa1405SMitch Phillips Analysis.parseSectionContents(
38999fa1405SMitch Phillips {
39099fa1405SMitch Phillips 0x75, 0x06, // 0: jne 8 [+6]
39199fa1405SMitch Phillips 0x90, // 2: nop
39299fa1405SMitch Phillips 0x90, // 3: nop
39399fa1405SMitch Phillips 0x90, // 4: nop
39499fa1405SMitch Phillips 0x90, // 5: nop
39599fa1405SMitch Phillips 0xff, 0x10, // 6: callq *(%rax)
39699fa1405SMitch Phillips 0x0f, 0x0b, // 8: ud2
39799fa1405SMitch Phillips },
39877fc1f60SAlexey Lapshin {0xDEADBEEF, 0x0});
39999fa1405SMitch Phillips uint64_t PrevSearchLengthForConditionalBranch =
40099fa1405SMitch Phillips SearchLengthForConditionalBranch;
40199fa1405SMitch Phillips SearchLengthForConditionalBranch = 2;
40299fa1405SMitch Phillips
40377fc1f60SAlexey Lapshin GraphResult Result =
40477fc1f60SAlexey Lapshin GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 6, 0x0});
40599fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, SizeIs(1));
40699fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes,
40799fa1405SMitch Phillips Each(HasPath(Result, ElementsAre(0xDEADBEEF + 4, 0xDEADBEEF + 5,
40899fa1405SMitch Phillips 0xDEADBEEF + 6))))
40999fa1405SMitch Phillips << PrintToString(Result);
41099fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, IsEmpty());
41199fa1405SMitch Phillips
41299fa1405SMitch Phillips SearchLengthForConditionalBranch = PrevSearchLengthForConditionalBranch;
41399fa1405SMitch Phillips }
41499fa1405SMitch Phillips
TEST_F(BasicGraphBuilderTest,BuildFlowGraphLengthExceededDownwards)41599fa1405SMitch Phillips TEST_F(BasicGraphBuilderTest, BuildFlowGraphLengthExceededDownwards) {
416d9af383dSMitch Phillips if (!SuccessfullyInitialised)
417*7fc87159SPaul Robinson GTEST_SKIP();
41899fa1405SMitch Phillips Analysis.parseSectionContents(
41999fa1405SMitch Phillips {
42099fa1405SMitch Phillips 0x75, 0x02, // 0: jne 4 [+2]
42199fa1405SMitch Phillips 0xff, 0x10, // 2: callq *(%rax)
42299fa1405SMitch Phillips 0x90, // 4: nop
42399fa1405SMitch Phillips 0x90, // 5: nop
42499fa1405SMitch Phillips 0x90, // 6: nop
42599fa1405SMitch Phillips 0x90, // 7: nop
42699fa1405SMitch Phillips 0x0f, 0x0b, // 8: ud2
42799fa1405SMitch Phillips },
42877fc1f60SAlexey Lapshin {0xDEADBEEF, 0x0});
42999fa1405SMitch Phillips uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;
43099fa1405SMitch Phillips SearchLengthForUndef = 2;
43199fa1405SMitch Phillips
43277fc1f60SAlexey Lapshin GraphResult Result =
43377fc1f60SAlexey Lapshin GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});
43499fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
43599fa1405SMitch Phillips EXPECT_THAT(
43699fa1405SMitch Phillips Result.ConditionalBranchNodes,
43799fa1405SMitch Phillips Each(AllOf(
43899fa1405SMitch Phillips Field(&ConditionalBranchNode::CFIProtection, Eq(false)),
43999fa1405SMitch Phillips Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
44099fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
44199fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 4, 0xDEADBEEF + 5))),
44299fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
44399fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 2))))))
44499fa1405SMitch Phillips << PrintToString(Result);
44599fa1405SMitch Phillips
44699fa1405SMitch Phillips SearchLengthForUndef = PrevSearchLengthForUndef;
44799fa1405SMitch Phillips }
44899fa1405SMitch Phillips
44999fa1405SMitch Phillips // This test ensures when avoiding doing repeated work we still generate the
45099fa1405SMitch Phillips // paths correctly. We don't need to recalculate the flow from 0x2 -> 0x3 as it
45199fa1405SMitch Phillips // should only need to be generated once.
TEST_F(BasicGraphBuilderTest,BuildFlowGraphWithRepeatedWork)45299fa1405SMitch Phillips TEST_F(BasicGraphBuilderTest, BuildFlowGraphWithRepeatedWork) {
453d9af383dSMitch Phillips if (!SuccessfullyInitialised)
454*7fc87159SPaul Robinson GTEST_SKIP();
45599fa1405SMitch Phillips Analysis.parseSectionContents(
45699fa1405SMitch Phillips {
45799fa1405SMitch Phillips 0x75, 0x05, // 0: jne 7 [+5]
45899fa1405SMitch Phillips 0x90, // 2: nop
45999fa1405SMitch Phillips 0xff, 0x10, // 3: callq *(%rax)
46099fa1405SMitch Phillips 0x75, 0xfb, // 5: jne 2 [-5]
46199fa1405SMitch Phillips 0x0f, 0x0b, // 7: ud2
46299fa1405SMitch Phillips },
46377fc1f60SAlexey Lapshin {0xDEADBEEF, 0x0});
46477fc1f60SAlexey Lapshin GraphResult Result =
46577fc1f60SAlexey Lapshin GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});
46699fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, IsEmpty());
46799fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(2));
46899fa1405SMitch Phillips EXPECT_THAT(
46999fa1405SMitch Phillips Result.ConditionalBranchNodes,
47099fa1405SMitch Phillips Contains(AllOf(
47199fa1405SMitch Phillips Field(&ConditionalBranchNode::CFIProtection, Eq(true)),
47299fa1405SMitch Phillips Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF)),
47399fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
47499fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 7))),
47599fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
47699fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))))))
47799fa1405SMitch Phillips << PrintToString(Result);
47899fa1405SMitch Phillips EXPECT_THAT(
47999fa1405SMitch Phillips Result.ConditionalBranchNodes,
48099fa1405SMitch Phillips Contains(AllOf(
48199fa1405SMitch Phillips Field(&ConditionalBranchNode::CFIProtection, Eq(true)),
48299fa1405SMitch Phillips Field(&ConditionalBranchNode::Address, Eq(0xDEADBEEF + 5)),
48399fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
48499fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 2, 0xDEADBEEF + 3))),
48599fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
48699fa1405SMitch Phillips HasPath(Result, ElementsAre(0xDEADBEEF + 7))))))
48799fa1405SMitch Phillips << PrintToString(Result);
48899fa1405SMitch Phillips EXPECT_THAT(Result.IntermediateNodes, SizeIs(1));
48999fa1405SMitch Phillips EXPECT_THAT(Result.IntermediateNodes,
49099fa1405SMitch Phillips UnorderedElementsAre(Pair(0xDEADBEEF + 2, 0xDEADBEEF + 3)));
49199fa1405SMitch Phillips }
49299fa1405SMitch Phillips
TEST_F(BasicGraphBuilderTest,BuildFlowGraphComplexExample)49399fa1405SMitch Phillips TEST_F(BasicGraphBuilderTest, BuildFlowGraphComplexExample) {
494d9af383dSMitch Phillips if (!SuccessfullyInitialised)
495*7fc87159SPaul Robinson GTEST_SKIP();
49699fa1405SMitch Phillips // The following code has this graph:
49799fa1405SMitch Phillips // +----------+ +--------------+
49899fa1405SMitch Phillips // | 20 | <--- | 0 |
49999fa1405SMitch Phillips // +----------+ +--------------+
50099fa1405SMitch Phillips // | |
50199fa1405SMitch Phillips // v v
50299fa1405SMitch Phillips // +----------+ +--------------+
50399fa1405SMitch Phillips // | 21 | | 2 |
50499fa1405SMitch Phillips // +----------+ +--------------+
50599fa1405SMitch Phillips // | |
50699fa1405SMitch Phillips // v v
50799fa1405SMitch Phillips // +----------+ +--------------+
50899fa1405SMitch Phillips // | 22 (ud2) | +-> | 7 |
50999fa1405SMitch Phillips // +----------+ | +--------------+
51099fa1405SMitch Phillips // ^ | |
51199fa1405SMitch Phillips // | | v
51299fa1405SMitch Phillips // +----------+ | +--------------+
51399fa1405SMitch Phillips // | 4 | | | 8 |
51499fa1405SMitch Phillips // +----------+ | +--------------+
51599fa1405SMitch Phillips // | | |
51699fa1405SMitch Phillips // v | v
51799fa1405SMitch Phillips // +----------+ | +--------------+ +------------+
51899fa1405SMitch Phillips // | 6 | -+ | 9 (indirect) | <- | 13 |
51999fa1405SMitch Phillips // +----------+ +--------------+ +------------+
52099fa1405SMitch Phillips // ^ |
52199fa1405SMitch Phillips // | v
52299fa1405SMitch Phillips // +--------------+ +------------+
52399fa1405SMitch Phillips // | 11 | | 15 (error) |
52499fa1405SMitch Phillips // +--------------+ +------------+
52599fa1405SMitch Phillips // Or, in image format: https://i.imgur.com/aX5fCoi.png
52699fa1405SMitch Phillips
52799fa1405SMitch Phillips Analysis.parseSectionContents(
52899fa1405SMitch Phillips {
52999fa1405SMitch Phillips 0x75, 0x12, // 0: jne 20 [+18]
53099fa1405SMitch Phillips 0xeb, 0x03, // 2: jmp 7 [+3]
53199fa1405SMitch Phillips 0x75, 0x10, // 4: jne 22 [+16]
53299fa1405SMitch Phillips 0x90, // 6: nop
53399fa1405SMitch Phillips 0x90, // 7: nop
53499fa1405SMitch Phillips 0x90, // 8: nop
53599fa1405SMitch Phillips 0xff, 0x10, // 9: callq *(%rax)
53699fa1405SMitch Phillips 0xeb, 0xfc, // 11: jmp 9 [-4]
53799fa1405SMitch Phillips 0x75, 0xfa, // 13: jne 9 [-6]
53899fa1405SMitch Phillips 0xe8, 0x78, 0x56, 0x34, 0x12, // 15: callq OUTOFBOUNDS [+0x12345678]
53999fa1405SMitch Phillips 0x90, // 20: nop
54099fa1405SMitch Phillips 0x90, // 21: nop
54199fa1405SMitch Phillips 0x0f, 0x0b, // 22: ud2
54299fa1405SMitch Phillips },
54377fc1f60SAlexey Lapshin {0x1000, 0x0});
54499fa1405SMitch Phillips uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;
54599fa1405SMitch Phillips SearchLengthForUndef = 5;
54699fa1405SMitch Phillips
54777fc1f60SAlexey Lapshin GraphResult Result =
54877fc1f60SAlexey Lapshin GraphBuilder::buildFlowGraph(Analysis, {0x1000 + 9, 0x0});
54999fa1405SMitch Phillips
55099fa1405SMitch Phillips EXPECT_THAT(Result.OrphanedNodes, SizeIs(1));
55199fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes, SizeIs(3));
55299fa1405SMitch Phillips
55399fa1405SMitch Phillips EXPECT_THAT(
55499fa1405SMitch Phillips Result.OrphanedNodes,
55599fa1405SMitch Phillips Each(AllOf(Eq(0x1000u + 11),
55699fa1405SMitch Phillips HasPath(Result, ElementsAre(0x1000 + 11, 0x1000 + 9)))))
55799fa1405SMitch Phillips << PrintToString(Result);
55899fa1405SMitch Phillips
55999fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes,
56099fa1405SMitch Phillips Contains(AllOf(
56199fa1405SMitch Phillips Field(&ConditionalBranchNode::CFIProtection, Eq(true)),
56299fa1405SMitch Phillips Field(&ConditionalBranchNode::Address, Eq(0x1000u)),
56399fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
56499fa1405SMitch Phillips HasPath(Result, ElementsAre(0x1000 + 20, 0x1000 + 21,
56599fa1405SMitch Phillips 0x1000 + 22))),
56699fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
56799fa1405SMitch Phillips HasPath(Result, ElementsAre(0x1000 + 2, 0x1000 + 7,
56899fa1405SMitch Phillips 0x1000 + 8, 0x1000 + 9))))))
56999fa1405SMitch Phillips << PrintToString(Result);
57099fa1405SMitch Phillips
57199fa1405SMitch Phillips EXPECT_THAT(Result.ConditionalBranchNodes,
57299fa1405SMitch Phillips Contains(AllOf(
57399fa1405SMitch Phillips Field(&ConditionalBranchNode::CFIProtection, Eq(true)),
57499fa1405SMitch Phillips Field(&ConditionalBranchNode::Address, Eq(0x1000u + 4)),
57599fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
57699fa1405SMitch Phillips HasPath(Result, ElementsAre(0x1000 + 22))),
57799fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
57899fa1405SMitch Phillips HasPath(Result, ElementsAre(0x1000 + 6, 0x1000 + 7,
57999fa1405SMitch Phillips 0x1000 + 8, 0x1000 + 9))))))
58099fa1405SMitch Phillips << PrintToString(Result);
58199fa1405SMitch Phillips
58299fa1405SMitch Phillips EXPECT_THAT(
58399fa1405SMitch Phillips Result.ConditionalBranchNodes,
58499fa1405SMitch Phillips Contains(AllOf(Field(&ConditionalBranchNode::CFIProtection, Eq(false)),
58599fa1405SMitch Phillips Field(&ConditionalBranchNode::Address, Eq(0x1000u + 13)),
58699fa1405SMitch Phillips Field(&ConditionalBranchNode::Target,
58799fa1405SMitch Phillips HasPath(Result, ElementsAre(0x1000 + 9))),
58899fa1405SMitch Phillips Field(&ConditionalBranchNode::Fallthrough,
58999fa1405SMitch Phillips HasPath(Result, ElementsAre(0x1000 + 15))))))
59099fa1405SMitch Phillips << PrintToString(Result);
59199fa1405SMitch Phillips
59299fa1405SMitch Phillips SearchLengthForUndef = PrevSearchLengthForUndef;
59399fa1405SMitch Phillips }
59499fa1405SMitch Phillips
59599fa1405SMitch Phillips } // anonymous namespace
59699fa1405SMitch Phillips } // end namespace cfi_verify
59799fa1405SMitch Phillips } // end namespace llvm
598