#include "../tools/llvm-cfi-verify/lib/FileAnalysis.h"
#include "../tools/llvm-cfi-verify/lib/GraphBuilder.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrAnalysis.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdlib>
using Instr = ::llvm::cfi_verify::FileAnalysis::Instr;
using ::testing::Eq;
using ::testing::Field;
namespace llvm {
namespace cfi_verify {
namespace {
class ELFTestFileAnalysis : public FileAnalysis {
public:
ELFTestFileAnalysis(StringRef Trip)
: FileAnalysis(Triple(Trip), SubtargetFeatures()) {}
void parseSectionContents(ArrayRef<uint8_t> SectionBytes,
object::SectionedAddress Address) {
FileAnalysis::parseSectionContents(SectionBytes, Address);
}
Error initialiseDisassemblyMembers() {
return FileAnalysis::initialiseDisassemblyMembers();
}
};
class BasicFileAnalysisTest : public ::testing::Test {
public:
BasicFileAnalysisTest(StringRef Trip)
: SuccessfullyInitialised(false), Analysis(Trip) {}
protected:
void SetUp() override {
IgnoreDWARFFlag = true;
SuccessfullyInitialised = true;
if (auto Err = Analysis.initialiseDisassemblyMembers()) {
handleAllErrors(std::move(Err), [&](const UnsupportedDisassembly &E) {
SuccessfullyInitialised = false;
outs()
<< "Note: CFIVerifyTests are disabled due to lack of support "
"on this build.\n";
});
}
}
bool SuccessfullyInitialised;
ELFTestFileAnalysis Analysis;
};
class BasicX86FileAnalysisTest : public BasicFileAnalysisTest {
public:
BasicX86FileAnalysisTest() : BasicFileAnalysisTest("x86_64--") {}
};
class BasicAArch64FileAnalysisTest : public BasicFileAnalysisTest {
public:
BasicAArch64FileAnalysisTest() : BasicFileAnalysisTest("aarch64--") {}
};
TEST_F(BasicX86FileAnalysisTest, BasicDisassemblyTraversalTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x90, 0xb0, 0x00, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x18, 0x48, 0xbe, 0xc4, 0x07, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x41, 0x0e, 0x62, 0x72, 0x65, 0x61, 0x6b, },
{0xDEADBEEF, 0x0});
EXPECT_EQ(nullptr, Analysis.getInstruction(0x0));
EXPECT_EQ(nullptr, Analysis.getInstruction(0x1000));
const auto *InstrMeta = Analysis.getInstruction(0xDEADBEEF);
EXPECT_NE(nullptr, InstrMeta);
EXPECT_EQ(0xDEADBEEF, InstrMeta->VMAddress);
EXPECT_EQ(1u, InstrMeta->InstructionSize);
EXPECT_TRUE(InstrMeta->Valid);
const auto *NextInstrMeta = Analysis.getNextInstructionSequential(*InstrMeta);
EXPECT_EQ(nullptr, Analysis.getPrevInstructionSequential(*InstrMeta));
const auto *PrevInstrMeta = InstrMeta;
InstrMeta = Analysis.getInstruction(0xDEADBEEF + 1);
EXPECT_NE(nullptr, InstrMeta);
EXPECT_EQ(NextInstrMeta, InstrMeta);
EXPECT_EQ(0xDEADBEEF + 1, InstrMeta->VMAddress);
EXPECT_EQ(2u, InstrMeta->InstructionSize);
EXPECT_TRUE(InstrMeta->Valid);
NextInstrMeta = Analysis.getNextInstructionSequential(*InstrMeta);
EXPECT_EQ(PrevInstrMeta, Analysis.getPrevInstructionSequential(*InstrMeta));
PrevInstrMeta = InstrMeta;
InstrMeta = Analysis.getInstruction(0xDEADBEEF + 3);
EXPECT_NE(nullptr, InstrMeta);
EXPECT_EQ(NextInstrMeta, InstrMeta);
EXPECT_EQ(0xDEADBEEF + 3, InstrMeta->VMAddress);
EXPECT_EQ(3u, InstrMeta->InstructionSize);
EXPECT_TRUE(InstrMeta->Valid);
NextInstrMeta = Analysis.getNextInstructionSequential(*InstrMeta);
EXPECT_EQ(PrevInstrMeta, Analysis.getPrevInstructionSequential(*InstrMeta));
PrevInstrMeta = InstrMeta;
InstrMeta = Analysis.getInstruction(0xDEADBEEF + 6);
EXPECT_NE(nullptr, InstrMeta);
EXPECT_EQ(NextInstrMeta, InstrMeta);
EXPECT_EQ(0xDEADBEEF + 6, InstrMeta->VMAddress);
EXPECT_EQ(4u, InstrMeta->InstructionSize);
EXPECT_TRUE(InstrMeta->Valid);
NextInstrMeta = Analysis.getNextInstructionSequential(*InstrMeta);
EXPECT_EQ(PrevInstrMeta, Analysis.getPrevInstructionSequential(*InstrMeta));
PrevInstrMeta = InstrMeta;
InstrMeta = Analysis.getInstruction(0xDEADBEEF + 10);
EXPECT_NE(nullptr, InstrMeta);
EXPECT_EQ(NextInstrMeta, InstrMeta);
EXPECT_EQ(0xDEADBEEF + 10, InstrMeta->VMAddress);
EXPECT_EQ(10u, InstrMeta->InstructionSize);
EXPECT_TRUE(InstrMeta->Valid);
EXPECT_EQ(nullptr, Analysis.getNextInstructionSequential(*InstrMeta));
EXPECT_EQ(PrevInstrMeta, Analysis.getPrevInstructionSequential(*InstrMeta));
PrevInstrMeta = InstrMeta;
InstrMeta = Analysis.getInstruction(0xDEADBEEF + 20);
EXPECT_NE(nullptr, InstrMeta);
EXPECT_EQ(0xDEADBEEF + 20, InstrMeta->VMAddress);
EXPECT_EQ(1u, InstrMeta->InstructionSize);
EXPECT_FALSE(InstrMeta->Valid);
EXPECT_EQ(nullptr, Analysis.getNextInstructionSequential(*InstrMeta));
EXPECT_EQ(PrevInstrMeta, Analysis.getPrevInstructionSequential(*InstrMeta));
InstrMeta = Analysis.getInstruction(0xDEADBEEF + 21);
EXPECT_NE(nullptr, InstrMeta);
EXPECT_EQ(0xDEADBEEF + 21, InstrMeta->VMAddress);
EXPECT_EQ(2u, InstrMeta->InstructionSize);
EXPECT_FALSE(InstrMeta->Valid);
EXPECT_EQ(nullptr, Analysis.getNextInstructionSequential(*InstrMeta));
EXPECT_EQ(nullptr, Analysis.getPrevInstructionSequential(*InstrMeta));
InstrMeta = Analysis.getInstruction(0xDEADBEEF + 23);
EXPECT_NE(nullptr, InstrMeta);
EXPECT_EQ(0xDEADBEEF + 23, InstrMeta->VMAddress);
EXPECT_EQ(5u, InstrMeta->InstructionSize);
EXPECT_FALSE(InstrMeta->Valid);
EXPECT_EQ(nullptr, Analysis.getNextInstructionSequential(*InstrMeta));
EXPECT_EQ(nullptr, Analysis.getPrevInstructionSequential(*InstrMeta));
}
TEST_F(BasicX86FileAnalysisTest, PrevAndNextFromBadInst) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x90, 0x2f, 0x90 },
{0xDEADBEEF, 0x0});
const auto &BadInstrMeta = Analysis.getInstructionOrDie(0xDEADBEEF + 1);
const auto *GoodInstrMeta =
Analysis.getPrevInstructionSequential(BadInstrMeta);
EXPECT_NE(nullptr, GoodInstrMeta);
EXPECT_EQ(0xDEADBEEF, GoodInstrMeta->VMAddress);
EXPECT_EQ(1u, GoodInstrMeta->InstructionSize);
GoodInstrMeta = Analysis.getNextInstructionSequential(BadInstrMeta);
EXPECT_NE(nullptr, GoodInstrMeta);
EXPECT_EQ(0xDEADBEEF + 2, GoodInstrMeta->VMAddress);
EXPECT_EQ(1u, GoodInstrMeta->InstructionSize);
}
TEST_F(BasicX86FileAnalysisTest, CFITrapTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x90, 0xb0, 0x00, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x18, 0x48, 0xbe, 0xc4, 0x07, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x41, 0x0e, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x0f, 0x0b },
{0xDEADBEEF, 0x0});
EXPECT_FALSE(Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF)));
EXPECT_FALSE(
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 3)));
EXPECT_FALSE(
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 6)));
EXPECT_FALSE(
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 10)));
EXPECT_FALSE(
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 20)));
EXPECT_FALSE(
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 21)));
EXPECT_FALSE(
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 23)));
EXPECT_TRUE(
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 28)));
}
TEST_F(BasicX86FileAnalysisTest, FallThroughTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x90, 0xb0, 0x00, 0x2f, 0x0f, 0x0b, 0xff, 0x20, 0xeb, 0x00, 0xe8, 0x45, 0xfe, 0xff, 0xff, 0xff, 0x10, 0x75, 0x00, 0xc3, },
{0xDEADBEEF, 0x0});
EXPECT_TRUE(
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF)));
EXPECT_TRUE(
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 1)));
EXPECT_FALSE(
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 3)));
EXPECT_FALSE(
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 4)));
EXPECT_FALSE(
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 6)));
EXPECT_FALSE(
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 8)));
EXPECT_FALSE(
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 10)));
EXPECT_FALSE(
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 15)));
EXPECT_TRUE(
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 17)));
EXPECT_FALSE(
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 19)));
}
TEST_F(BasicX86FileAnalysisTest, DefiniteNextInstructionTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x90, 0xb0, 0x00, 0x2f, 0x0f, 0x0b, 0xff, 0x20, 0xeb, 0x00, 0xeb, 0x05, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x78, 0x56, 0x34, 0x12, 0xe8, 0x04, 0x00, 0x00, 0x00, 0xff, 0x10, 0x75, 0x00, 0x75, 0xe0, 0xc3, 0xeb, 0xdd, 0xeb, 0xdd, 0xeb, 0xdc, },
{0xDEADBEEF, 0x0});
const auto *Current = Analysis.getInstruction(0xDEADBEEF);
const auto *Next = Analysis.getDefiniteNextInstruction(*Current);
EXPECT_NE(nullptr, Next);
EXPECT_EQ(0xDEADBEEF + 1, Next->VMAddress);
Current = Analysis.getInstruction(0xDEADBEEF + 1);
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
Current = Analysis.getInstruction(0xDEADBEEF + 3);
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
Current = Analysis.getInstruction(0xDEADBEEF + 4);
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
Current = Analysis.getInstruction(0xDEADBEEF + 6);
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
Current = Analysis.getInstruction(0xDEADBEEF + 8);
Next = Analysis.getDefiniteNextInstruction(*Current);
EXPECT_NE(nullptr, Next);
EXPECT_EQ(0xDEADBEEF + 10, Next->VMAddress);
Current = Analysis.getInstruction(0xDEADBEEF + 10);
Next = Analysis.getDefiniteNextInstruction(*Current);
EXPECT_NE(nullptr, Next);
EXPECT_EQ(0xDEADBEEF + 17, Next->VMAddress);
Current = Analysis.getInstruction(0xDEADBEEF + 12);
Next = Analysis.getDefiniteNextInstruction(*Current);
EXPECT_NE(nullptr, Next);
EXPECT_EQ(0xDEADBEEF + 17, Next->VMAddress);
Current = Analysis.getInstruction(0xDEADBEEF + 17);
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
Next = Analysis.getDefiniteNextInstruction(*Current);
Current = Analysis.getInstruction(0xDEADBEEF + 22);
Next = Analysis.getDefiniteNextInstruction(*Current);
EXPECT_NE(nullptr, Next);
EXPECT_EQ(0xDEADBEEF + 31, Next->VMAddress);
Current = Analysis.getInstruction(0xDEADBEEF + 27);
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
Current = Analysis.getInstruction(0xDEADBEEF + 29);
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
Current = Analysis.getInstruction(0xDEADBEEF + 31);
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
Current = Analysis.getInstruction(0xDEADBEEF + 33);
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
Current = Analysis.getInstruction(0xDEADBEEF + 34);
Next = Analysis.getDefiniteNextInstruction(*Current);
EXPECT_NE(nullptr, Next);
EXPECT_EQ(0xDEADBEEF + 1, Next->VMAddress);
Current = Analysis.getInstruction(0xDEADBEEF + 36);
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
Current = Analysis.getInstruction(0xDEADBEEF + 38);
Next = Analysis.getDefiniteNextInstruction(*Current);
EXPECT_NE(nullptr, Next);
EXPECT_EQ(0xDEADBEEF + 4, Next->VMAddress);
}
TEST_F(BasicX86FileAnalysisTest, ControlFlowXRefsTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x90, 0xb0, 0x00, 0x2f, 0x0f, 0x0b, 0xff, 0x20, 0xeb, 0x00, 0xeb, 0x05, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x78, 0x56, 0x34, 0x12, 0xe8, 0x04, 0x00, 0x00, 0x00, 0xff, 0x10, 0x75, 0x00, 0x75, 0xe0, 0xc3, 0xeb, 0xdd, 0xeb, 0xdd, 0xeb, 0xdc, },
{0xDEADBEEF, 0x0});
const auto *InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF);
std::set<const Instr *> XRefs =
Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_TRUE(XRefs.empty());
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 1);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_THAT(XRefs, UnorderedElementsAre(
Field(&Instr::VMAddress, Eq(0xDEADBEEF)),
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 31)),
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 34))));
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 3);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_THAT(XRefs, UnorderedElementsAre(
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 1)),
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 36))));
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 4);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_THAT(XRefs, UnorderedElementsAre(
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 38))));
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 6);
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 8);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 10);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_THAT(XRefs, UnorderedElementsAre(
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 8))));
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 12);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 17);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_THAT(XRefs, UnorderedElementsAre(
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 10)),
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 12))));
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 22);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 27);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 29);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 31);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_THAT(XRefs, UnorderedElementsAre(
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 22)),
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 29))));
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 33);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_THAT(XRefs, UnorderedElementsAre(
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 31))));
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 34);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 36);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 38);
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionInvalidTargets) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x90, 0x0f, 0x0b, 0x75, 0x00, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_NOT_INDIRECT_CF,
Analysis.validateCFIProtection(Result));
Result = GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 1, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_NOT_INDIRECT_CF,
Analysis.validateCFIProtection(Result));
Result = GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_NOT_INDIRECT_CF,
Analysis.validateCFIProtection(Result));
Result = GraphBuilder::buildFlowGraph(Analysis, {0x12345678, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_INVALID_INSTRUCTION,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionBasicFallthroughToUd2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x75, 0x02, 0x0f, 0x0b, 0xff, 0x10, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionBasicJumpToUd2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x75, 0x02, 0xff, 0x10, 0x0f, 0x0b, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualPathUd2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x75, 0x03, 0x90, 0xff, 0x10, 0x0f, 0x0b, 0x75, 0xf9, 0x0f, 0x0b, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualPathSingleUd2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x75, 0x05, 0x90, 0xff, 0x10, 0x75, 0xfb, 0x0f, 0x0b, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 3, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualFailLimitUpwards) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x75, 0x06, 0x90, 0x90, 0x90, 0x90, 0xff, 0x10, 0x0f, 0x0b, },
{0xDEADBEEF, 0x0});
uint64_t PrevSearchLengthForConditionalBranch =
SearchLengthForConditionalBranch;
SearchLengthForConditionalBranch = 2;
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 6, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,
Analysis.validateCFIProtection(Result));
SearchLengthForConditionalBranch = PrevSearchLengthForConditionalBranch;
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualFailLimitDownwards) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x75, 0x02, 0xff, 0x10, 0x90, 0x90, 0x90, 0x90, 0x0f, 0x0b, },
{0xDEADBEEF, 0x0});
uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;
SearchLengthForUndef = 2;
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 2, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_BAD_CONDITIONAL_BRANCH,
Analysis.validateCFIProtection(Result));
SearchLengthForUndef = PrevSearchLengthForUndef;
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionGoodAndBadPaths) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0xeb, 0x02, 0x75, 0x02, 0xff, 0x10, 0x0f, 0x0b, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionWithUnconditionalJumpInFallthrough) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x75, 0x04, 0xeb, 0x00, 0xff, 0x10, 0x0f, 0x0b, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionComplexExample) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x75, 0x12, 0xeb, 0x03, 0x75, 0x10, 0x90, 0x90, 0x90, 0xff, 0x10, 0xeb, 0xfc, 0x75, 0xfa, 0xe8, 0x78, 0x56, 0x34, 0x12, 0x90, 0x90, 0x0f, 0x0b, },
{0xDEADBEEF, 0x0});
uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;
SearchLengthForUndef = 5;
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 9, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,
Analysis.validateCFIProtection(Result));
SearchLengthForUndef = PrevSearchLengthForUndef;
}
TEST_F(BasicX86FileAnalysisTest, UndefSearchLengthOneTest) {
Analysis.parseSectionContents(
{
0x77, 0x0d, 0x48, 0x89, 0xdf, 0xff, 0xd0, 0x48, 0x89, 0xdf, 0xe8, 0x09, 0x00, 0x00, 0x00, 0x0f, 0x0b, },
{0x688118, 0x0});
uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;
SearchLengthForUndef = 1;
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, {0x68811d, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
SearchLengthForUndef = PrevSearchLengthForUndef;
}
TEST_F(BasicX86FileAnalysisTest, UndefSearchLengthOneTestFarAway) {
Analysis.parseSectionContents(
{
0x74, 0x73, 0xe9, 0x1c, 0x04, 0x00, 0x00, 0x00, },
{0x7759eb, 0x0});
Analysis.parseSectionContents(
{
0x0f, 0x85, 0xb2, 0x03, 0x00, 0x00, 0x48, 0x83, 0xc3, 0xf4, 0x48, 0x8b, 0x7c, 0x24, 0x10, 0x48, 0x89, 0xde, 0xff, 0xd1, },
{0x775a56, 0x0});
Analysis.parseSectionContents(
{
0x0f, 0x0b, },
{0x775e0e, 0x0});
uint64_t PrevSearchLengthForUndef = SearchLengthForUndef;
SearchLengthForUndef = 1;
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, {0x775a68, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_BAD_CONDITIONAL_BRANCH,
Analysis.validateCFIProtection(Result));
SearchLengthForUndef = 2;
Result = GraphBuilder::buildFlowGraph(Analysis, {0x775a68, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
SearchLengthForUndef = 3;
Result = GraphBuilder::buildFlowGraph(Analysis, {0x775a68, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
SearchLengthForUndef = PrevSearchLengthForUndef;
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathExplicit) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x75, 0x02, 0x0f, 0x0b, 0x48, 0x05, 0x00, 0x00, 0x00, 0x00, 0xff, 0x10, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 10, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathExplicit2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x75, 0x02, 0x0f, 0x0b, 0x48, 0x83, 0xc0, 0x00, 0xff, 0x10, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 8, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathImplicit) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x75, 0x02, 0x0f, 0x0b, 0x05, 0x00, 0x00, 0x00, 0x00, 0xff, 0x10, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 9, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberDualPathImplicit) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x75, 0x04, 0x0f, 0x31, 0xff, 0x10, 0x0f, 0x0b, 0x75, 0xf9, 0x0f, 0x0b, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 4, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64BasicUnprotected) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x00, 0x01, 0x3f, 0xd6, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64BasicProtected) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, 0x20, 0x00, 0x20, 0xd4, 0x00, 0x01, 0x3f, 0xd6, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 8, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberBasic) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, 0x20, 0x00, 0x20, 0xd4, 0x08, 0x05, 0x00, 0x91, 0x00, 0x01, 0x3f, 0xd6, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 12, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberOneLoad) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, 0x20, 0x00, 0x20, 0xd4, 0x21, 0x09, 0x40, 0xf9, 0x20, 0x00, 0x1f, 0xd6, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 12, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddGood) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, 0x20, 0x00, 0x20, 0xd4, 0x21, 0x04, 0x00, 0x91, 0x21, 0x09, 0x40, 0xf9, 0x20, 0x00, 0x1f, 0xd6, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 16, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddBad) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, 0x20, 0x00, 0x20, 0xd4, 0x21, 0x09, 0x40, 0xf9, 0x21, 0x04, 0x00, 0x91, 0x20, 0x00, 0x1f, 0xd6, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 16, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddBad2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, 0x20, 0x00, 0x20, 0xd4, 0x29, 0x04, 0x00, 0x91, 0x21, 0x09, 0x40, 0xf9, 0x20, 0x00, 0x1f, 0xd6, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 16, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberTwoLoads) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, 0x20, 0x00, 0x20, 0xd4, 0x21, 0x09, 0x40, 0xf9, 0x21, 0x08, 0x40, 0xf9, 0x20, 0x00, 0x1f, 0xd6, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 16, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberUnrelatedSecondLoad) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, 0x20, 0x00, 0x20, 0xd4, 0x21, 0x09, 0x40, 0xf9, 0x21, 0x09, 0x40, 0xf9, 0x20, 0x00, 0x1f, 0xd6, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 16, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberUnrelatedLoads) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, 0x20, 0x00, 0x20, 0xd4, 0x22, 0x09, 0x40, 0xf9, 0x22, 0x08, 0x40, 0xf9, 0x20, 0x00, 0x1f, 0xd6, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 16, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64GoodAndBadPaths) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x03, 0x00, 0x00, 0x14, 0x49, 0x00, 0x00, 0x54, 0x20, 0x00, 0x20, 0xd4, 0x20, 0x00, 0x1f, 0xd6, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 12, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPaths) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0xc9, 0x00, 0x00, 0x54, 0x21, 0x08, 0x40, 0xf9, 0x03, 0x00, 0x00, 0x14, 0x69, 0x00, 0x00, 0x54, 0x21, 0x08, 0x40, 0xf9, 0x20, 0x00, 0x1f, 0xd6, 0x20, 0x00, 0x20, 0xd4, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 20, 0x0});
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPathsBadLoad1) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0xe9, 0x00, 0x00, 0x54, 0x21, 0x08, 0x40, 0xf9, 0x21, 0x08, 0x40, 0xf9, 0x03, 0x00, 0x00, 0x14, 0x69, 0x00, 0x00, 0x54, 0x21, 0x08, 0x40, 0xf9, 0x20, 0x00, 0x1f, 0xd6, 0x20, 0x00, 0x20, 0xd4, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 24, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPathsBadLoad2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0xe9, 0x00, 0x00, 0x54, 0x21, 0x08, 0x40, 0xf9, 0x03, 0x00, 0x00, 0x14, 0x89, 0x00, 0x00, 0x54, 0x21, 0x08, 0x40, 0xf9, 0x21, 0x08, 0x40, 0xf9, 0x20, 0x00, 0x1f, 0xd6, 0x20, 0x00, 0x20, 0xd4, },
{0xDEADBEEF, 0x0});
GraphResult Result =
GraphBuilder::buildFlowGraph(Analysis, {0xDEADBEEF + 24, 0x0});
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
} } }
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
llvm::cl::ParseCommandLineOptions(argc, argv);
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
llvm::InitializeAllDisassemblers();
return RUN_ALL_TESTS();
}