1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 10:42:39 +01:00

[cfi-verify] Support AArch64.

This patch adds support for AArch64 to cfi-verify.

This required three changes to cfi-verify.  First, it generalizes checking if an instruction is a trap by adding a new isTrap flag to TableGen (and defining it for x86 and AArch64).  Second, the code that ensures that the operand register is not clobbered between the CFI check and the indirect call needs to allow a single dereference (in x86 this happens as part of the jump instruction).  Third, we needed to ensure that return instructions are not counted as indirect branches.  Technically, returns are indirect branches and can be covered by CFI, but LLVM's forward-edge CFI does not protect them, and x86 does not consider them, so we keep that behavior.

In addition, we had to improve AArch64's code to evaluate the branch target of a MCInst to handle calls where the destination is not the first operand (which it often is not).

Differential Revision: https://reviews.llvm.org/D48836

llvm-svn: 337007
This commit is contained in:
Joel Galenson 2018-07-13 15:19:33 +00:00
parent 2705c2710a
commit 9249622410
14 changed files with 692 additions and 51 deletions

View File

@ -149,7 +149,8 @@ enum Flag {
ExtractSubreg,
InsertSubreg,
Convergent,
Add
Add,
Trap
};
}
@ -245,6 +246,9 @@ public:
/// Return true if the instruction is an add instruction.
bool isAdd() const { return Flags & (1ULL << MCID::Add); }
/// Return true if this instruction is a trap.
bool isTrap() const { return Flags & (1ULL << MCID::Trap); }
/// Return true if the instruction is a register to register move.
bool isMoveReg() const { return Flags & (1ULL << MCID::MoveReg); }

View File

@ -448,6 +448,7 @@ class Instruction {
bit isBarrier = 0; // Can control flow fall through this instruction?
bit isCall = 0; // Is this instruction a call instruction?
bit isAdd = 0; // Is this instruction an add instruction?
bit isTrap = 0; // Is this instruction a trap instruction?
bit canFoldAsLoad = 0; // Can this be folded as a simple memory operand?
bit mayLoad = ?; // Is it possible for this inst to read memory?
bit mayStore = ?; // Is it possible for this inst to write memory?

View File

@ -1442,7 +1442,9 @@ def : Pat<(AArch64call texternalsym:$func), (BL texternalsym:$func)>;
//===----------------------------------------------------------------------===//
// Exception generation instructions.
//===----------------------------------------------------------------------===//
let isTrap = 1 in {
def BRK : ExceptionGeneration<0b001, 0b00, "brk">;
}
def DCPS1 : ExceptionGeneration<0b101, 0b01, "dcps1">;
def DCPS2 : ExceptionGeneration<0b101, 0b10, "dcps2">;
def DCPS3 : ExceptionGeneration<0b101, 0b11, "dcps3">;

View File

@ -140,14 +140,18 @@ public:
bool evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size,
uint64_t &Target) const override {
if (Inst.getNumOperands() == 0 ||
Info->get(Inst.getOpcode()).OpInfo[0].OperandType !=
MCOI::OPERAND_PCREL)
return false;
int64_t Imm = Inst.getOperand(0).getImm() * 4;
Target = Addr + Imm;
return true;
// Search for a PC-relative argument.
// This will handle instructions like bcc (where the first argument is the
// condition code) and cbz (where it is a register).
const auto &Desc = Info->get(Inst.getOpcode());
for (unsigned i = 0, e = Inst.getNumOperands(); i != e; i++) {
if (Desc.OpInfo[i].OperandType == MCOI::OPERAND_PCREL) {
int64_t Imm = Inst.getOperand(i).getImm() * 4;
Target = Addr + Imm;
return true;
}
}
return false;
}
};

View File

@ -22,7 +22,7 @@ let Defs = [RAX, RCX, RDX] in
// CPU flow control instructions
let mayLoad = 1, mayStore = 0, hasSideEffects = 1 in {
let mayLoad = 1, mayStore = 0, hasSideEffects = 1, isTrap = 1 in {
def TRAP : I<0x0B, RawFrm, (outs), (ins), "ud2", [(trap)]>, TB;
def UD2B : I<0xB9, RawFrm, (outs), (ins), "ud2b", []>, TB;
}

View File

@ -0,0 +1,183 @@
# RUN: llvm-mc %s -filetype obj -triple aarch64-- -o %t.o
# RUN: llvm-cfi-verify %t.o | FileCheck %s
# CHECK-LABEL: {{^Instruction: .* \(PROTECTED\)}}
# CHECK-NEXT: tiny.cc:9
# CHECK: Expected Protected: 1 (100.00%)
# CHECK: Unexpected Protected: 0 (0.00%)
# CHECK: Expected Unprotected: 0 (0.00%)
# CHECK: Unexpected Unprotected (BAD): 0 (0.00%)
# Source (tiny.cc):
# int a() { return 42; }
# int b() { return 137; }
# int main(int argc, char** argv) {
# int(*ptr)();
# if (argc == 1)
# ptr = &a;
# else
# ptr = &b;
# return ptr();
# }
.text
.file "ld-temp.o"
.p2align 2
.type _Z1av.cfi,@function
_Z1av.cfi:
.Lfunc_begin0:
.file 1 "/tmp/tiny.cc"
.loc 1 1 0
.cfi_startproc
.loc 1 1 11 prologue_end
mov w0, #42
ret
.Ltmp0:
.Lfunc_end0:
.size _Z1av.cfi, .Lfunc_end0-_Z1av.cfi
.cfi_endproc
.p2align 2
.type _Z1bv.cfi,@function
_Z1bv.cfi:
.Lfunc_begin1:
.loc 1 2 0
.cfi_startproc
.loc 1 2 11 prologue_end
mov w0, #137
ret
.Ltmp1:
.Lfunc_end1:
.size _Z1bv.cfi, .Lfunc_end1-_Z1bv.cfi
.cfi_endproc
.p2align 2
.type main,@function
main:
.Lfunc_begin2:
.loc 1 3 0
.cfi_startproc
sub sp, sp, #48
stp x29, x30, [sp, #32]
add x29, sp, #32
.cfi_def_cfa w29, 16
.cfi_offset w30, -8
.cfi_offset w29, -16
stur wzr, [x29, #-4]
stur w0, [x29, #-8]
str x1, [sp, #16]
.Ltmp2:
.loc 1 5 7 prologue_end
ldur w8, [x29, #-8]
cmp w8, #1
b.ne .LBB2_2
.loc 1 0 7 is_stmt 0
adrp x8, _Z1av
add x8, x8, :lo12:_Z1av
.loc 1 6 9 is_stmt 1
str x8, [sp, #8]
.loc 1 6 5 is_stmt 0
b .LBB2_3
.LBB2_2:
.loc 1 0 5
adrp x8, _Z1bv
add x8, x8, :lo12:_Z1bv
.loc 1 8 9 is_stmt 1
str x8, [sp, #8]
.LBB2_3:
.loc 1 0 9 is_stmt 0
adrp x8, .L.cfi.jumptable
add x9, x8, :lo12:.L.cfi.jumptable
.loc 1 9 10 is_stmt 1
ldr x8, [sp, #8]
sub x9, x8, x9
lsr x10, x9, #2
orr x9, x10, x9, lsl #62
cmp x9, #1
b.ls .LBB2_5
brk #0x1
.LBB2_5:
blr x8
.loc 1 9 3 is_stmt 0
ldp x29, x30, [sp, #32]
add sp, sp, #48
ret
.Ltmp3:
.Lfunc_end2:
.size main, .Lfunc_end2-main
.cfi_endproc
.p2align 2
.type .L.cfi.jumptable,@function
.L.cfi.jumptable:
.Lfunc_begin3:
.cfi_startproc
//APP
b _Z1av.cfi
b _Z1bv.cfi
//NO_APP
.Lfunc_end3:
.size .L.cfi.jumptable, .Lfunc_end3-.L.cfi.jumptable
.cfi_endproc
.type .L__unnamed_1,@object
.section .rodata,"a",@progbits
.p2align 2
.L__unnamed_1:
.size .L__unnamed_1, 0
.section .debug_str,"MS",@progbits,1
.Linfo_string0:
.asciz "clang version 7.0.0 (trunk 335774) (llvm/trunk 335775)"
.Linfo_string1:
.asciz "tiny.cc"
.Linfo_string2:
.asciz ""
.section .debug_abbrev,"",@progbits
.byte 1
.byte 17
.byte 0
.byte 37
.byte 14
.byte 19
.byte 5
.byte 3
.byte 14
.byte 16
.byte 23
.byte 27
.byte 14
.byte 17
.byte 1
.byte 18
.byte 6
.byte 0
.byte 0
.byte 0
.section .debug_info,"",@progbits
.Lcu_begin0:
.word 38
.hword 4
.word .debug_abbrev
.byte 8
.byte 1
.word .Linfo_string0
.hword 4
.word .Linfo_string1
.word .Lline_table_start0
.word .Linfo_string2
.xword .Lfunc_begin0
.word .Lfunc_end2-.Lfunc_begin0
.section .debug_ranges,"",@progbits
.section .debug_macinfo,"",@progbits
.byte 0
.type _Z1av,@function
.set _Z1av, .L.cfi.jumptable
.type _Z1bv,@function
.set _Z1bv, .L.cfi.jumptable+4
.ident "clang version 7.0.0 (trunk 335774) (llvm/trunk 335775)"
.section ".note.GNU-stack","",@progbits
.section .debug_line,"",@progbits
.Lline_table_start0:

View File

@ -0,0 +1,169 @@
# RUN: llvm-mc %s -filetype obj -triple aarch64-- -o %t.o
# RUN: llvm-cfi-verify %t.o | FileCheck %s
# CHECK-LABEL: {{^Instruction: .* \(FAIL_BAD_CONDITIONAL_BRANCH\)}}
# CHECK-NEXT: tiny.cc:9
# CHECK: Expected Protected: 0 (0.00%)
# CHECK: Unexpected Protected: 0 (0.00%)
# CHECK: Expected Unprotected: 0 (0.00%)
# CHECK: Unexpected Unprotected (BAD): 1 (100.00%)
# Source (tiny.cc):
# int a() { return 42; }
# int b() { return 137; }
# int main(int argc, char** argv) {
# int(*ptr)();
# if (argc == 1)
# ptr = &a;
# else
# ptr = &b;
# return ptr();
# }
# Compile with:
# clang++ -target aarch64-- -gmlt tiny.cc -S -o tiny.s
.text
.file "tiny.cc"
.globl _Z1av
.p2align 2
.type _Z1av,@function
_Z1av: // @_Z1av
.Lfunc_begin0:
.file 1 "tiny.cc"
.loc 1 1 0 // tiny.cc:1:0
.cfi_startproc
// BB#0:
mov w0, #42
.Ltmp0:
.loc 1 1 11 prologue_end // tiny.cc:1:11
ret
.Ltmp1:
.Lfunc_end0:
.size _Z1av, .Lfunc_end0-_Z1av
.cfi_endproc
.globl _Z1bv
.p2align 2
.type _Z1bv,@function
_Z1bv: // @_Z1bv
.Lfunc_begin1:
.loc 1 2 0 // tiny.cc:2:0
.cfi_startproc
// BB#0:
mov w0, #137
.Ltmp2:
.loc 1 2 11 prologue_end // tiny.cc:2:11
ret
.Ltmp3:
.Lfunc_end1:
.size _Z1bv, .Lfunc_end1-_Z1bv
.cfi_endproc
.globl main
.p2align 2
.type main,@function
main: // @main
.Lfunc_begin2:
.loc 1 3 0 // tiny.cc:3:0
.cfi_startproc
// BB#0:
sub sp, sp, #48 // =48
stp x29, x30, [sp, #32] // 8-byte Folded Spill
add x29, sp, #32 // =32
.Lcfi0:
.cfi_def_cfa w29, 16
.Lcfi1:
.cfi_offset w30, -8
.Lcfi2:
.cfi_offset w29, -16
stur wzr, [x29, #-4]
stur w0, [x29, #-8]
str x1, [sp, #16]
.Ltmp4:
.loc 1 5 7 prologue_end // tiny.cc:5:7
ldur w0, [x29, #-8]
cmp w0, #1 // =1
b.ne .LBB2_2
// BB#1:
.loc 1 0 7 is_stmt 0 // tiny.cc:0:7
adrp x8, _Z1av
add x8, x8, :lo12:_Z1av
.loc 1 6 9 is_stmt 1 // tiny.cc:6:9
str x8, [sp, #8]
.loc 1 6 5 is_stmt 0 // tiny.cc:6:5
b .LBB2_3
.LBB2_2:
.loc 1 0 5 // tiny.cc:0:5
adrp x8, _Z1bv
add x8, x8, :lo12:_Z1bv
.loc 1 8 9 is_stmt 1 // tiny.cc:8:9
str x8, [sp, #8]
.LBB2_3:
.loc 1 9 10 // tiny.cc:9:10
ldr x8, [sp, #8]
blr x8
.loc 1 9 3 is_stmt 0 // tiny.cc:9:3
ldp x29, x30, [sp, #32] // 8-byte Folded Reload
add sp, sp, #48 // =48
ret
.Ltmp5:
.Lfunc_end2:
.size main, .Lfunc_end2-main
.cfi_endproc
.section .debug_str,"MS",@progbits,1
.Linfo_string0:
.asciz "clang version 4.0.1-10 (tags/RELEASE_401/final)" // string offset=0
.Linfo_string1:
.asciz "tiny.cc" // string offset=48
.Linfo_string2:
.asciz "/tmp" // string offset=56
.section .debug_loc,"",@progbits
.section .debug_abbrev,"",@progbits
.Lsection_abbrev:
.byte 1 // Abbreviation Code
.byte 17 // DW_TAG_compile_unit
.byte 0 // DW_CHILDREN_no
.byte 37 // DW_AT_producer
.byte 14 // DW_FORM_strp
.byte 19 // DW_AT_language
.byte 5 // DW_FORM_data2
.byte 3 // DW_AT_name
.byte 14 // DW_FORM_strp
.byte 16 // DW_AT_stmt_list
.byte 23 // DW_FORM_sec_offset
.byte 27 // DW_AT_comp_dir
.byte 14 // DW_FORM_strp
.byte 17 // DW_AT_low_pc
.byte 1 // DW_FORM_addr
.byte 18 // DW_AT_high_pc
.byte 6 // DW_FORM_data4
.byte 0 // EOM(1)
.byte 0 // EOM(2)
.byte 0 // EOM(3)
.section .debug_info,"",@progbits
.Lsection_info:
.Lcu_begin0:
.word 38 // Length of Unit
.hword 4 // DWARF version number
.word .Lsection_abbrev // Offset Into Abbrev. Section
.byte 8 // Address Size (in bytes)
.byte 1 // Abbrev [1] 0xb:0x1f DW_TAG_compile_unit
.word .Linfo_string0 // DW_AT_producer
.hword 4 // DW_AT_language
.word .Linfo_string1 // DW_AT_name
.word .Lline_table_start0 // DW_AT_stmt_list
.word .Linfo_string2 // DW_AT_comp_dir
.xword .Lfunc_begin0 // DW_AT_low_pc
.word .Lfunc_end2-.Lfunc_begin0 // DW_AT_high_pc
.section .debug_ranges,"",@progbits
.Ldebug_range:
.section .debug_macinfo,"",@progbits
.Ldebug_macinfo:
.Lcu_macro_begin0:
.byte 0 // End Of Macro List Mark
.ident "clang version 4.0.1-10 (tags/RELEASE_401/final)"
.section ".note.GNU-stack","",@progbits
.section .debug_line,"",@progbits
.Lline_table_start0:

View File

@ -154,7 +154,8 @@ const Instr &FileAnalysis::getInstructionOrDie(uint64_t Address) const {
}
bool FileAnalysis::isCFITrap(const Instr &InstrMeta) const {
return MII->getName(InstrMeta.Instruction.getOpcode()) == "TRAP";
const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
return InstrDesc.isTrap();
}
bool FileAnalysis::canFallThrough(const Instr &InstrMeta) const {
@ -296,20 +297,38 @@ uint64_t FileAnalysis::indirectCFOperandClobber(const GraphResult &Graph) const
else
Node = Branch.Fallthrough;
while (Node != Graph.BaseAddress) {
// Some architectures (e.g., AArch64) cannot load in an indirect branch, so
// we allow them one load.
bool canLoad = !MII->get(IndirectCF.Instruction.getOpcode()).mayLoad();
// We walk backwards from the indirect CF. It is the last node returned by
// Graph.flattenAddress, so we skip it since we already handled it.
DenseSet<unsigned> CurRegisterNumbers = RegisterNumbers;
std::vector<uint64_t> Nodes = Graph.flattenAddress(Node);
for (auto I = Nodes.rbegin() + 1, E = Nodes.rend(); I != E; ++I) {
Node = *I;
const Instr &NodeInstr = getInstructionOrDie(Node);
const auto &InstrDesc = MII->get(NodeInstr.Instruction.getOpcode());
for (unsigned RegNum : RegisterNumbers) {
for (auto RI = CurRegisterNumbers.begin(), RE = CurRegisterNumbers.end();
RI != RE; ++RI) {
unsigned RegNum = *RI;
if (InstrDesc.hasDefOfPhysReg(NodeInstr.Instruction, RegNum,
*RegisterInfo))
return Node;
*RegisterInfo)) {
if (!canLoad || !InstrDesc.mayLoad())
return Node;
canLoad = false;
CurRegisterNumbers.erase(RI);
// Add the registers this load reads to those we check for clobbers.
for (unsigned i = InstrDesc.getNumDefs(),
e = InstrDesc.getNumOperands(); i != e; i++) {
const auto Operand = NodeInstr.Instruction.getOperand(i);
if (Operand.isReg())
CurRegisterNumbers.insert(Operand.getReg());
}
break;
}
}
const auto &KV = Graph.IntermediateNodes.find(Node);
assert((KV != Graph.IntermediateNodes.end()) &&
"Could not get next node.");
Node = KV->second;
}
}
@ -456,6 +475,9 @@ void FileAnalysis::parseSectionContents(ArrayRef<uint8_t> SectionBytes,
if (!usesRegisterOperand(InstrMeta))
continue;
if (InstrDesc.isReturn())
continue;
// Check if this instruction exists in the range of the DWARF metadata.
if (!IgnoreDWARFFlag) {
auto LineInfo =

View File

@ -149,10 +149,13 @@ public:
CFIProtectionStatus validateCFIProtection(const GraphResult &Graph) const;
// Returns the first place the operand register is clobbered between the CFI-
// check and the indirect CF instruction execution. If the register is not
// modified, returns the address of the indirect CF instruction. The result is
// undefined if the provided graph does not fall under either the
// FAIL_REGISTER_CLOBBERED or PROTECTED status (see CFIProtectionStatus).
// check and the indirect CF instruction execution. We do this by walking
// backwards from the indirect CF and ensuring there is at most one load
// involving the operand register (which is the indirect CF itself on x86).
// If the register is not modified, returns the address of the indirect CF
// instruction. The result is undefined if the provided graph does not fall
// under either the FAIL_REGISTER_CLOBBERED or PROTECTED status (see
// CFIProtectionStatus).
uint64_t indirectCFOperandClobber(const GraphResult& Graph) const;
// Prints an instruction to the provided stream using this object's pretty-

View File

@ -45,10 +45,10 @@ using ::testing::Field;
namespace llvm {
namespace cfi_verify {
namespace {
class ELFx86TestFileAnalysis : public FileAnalysis {
class ELFTestFileAnalysis : public FileAnalysis {
public:
ELFx86TestFileAnalysis()
: FileAnalysis(Triple("x86_64--"), SubtargetFeatures()) {}
ELFTestFileAnalysis(StringRef Trip)
: FileAnalysis(Triple(Trip), SubtargetFeatures()) {}
// Expose this method publicly for testing.
void parseSectionContents(ArrayRef<uint8_t> SectionBytes,
@ -62,6 +62,8 @@ public:
};
class BasicFileAnalysisTest : public ::testing::Test {
public:
BasicFileAnalysisTest(StringRef Trip) : Analysis(Trip) {}
protected:
virtual void SetUp() {
IgnoreDWARFFlag = true;
@ -70,17 +72,27 @@ protected:
handleAllErrors(std::move(Err), [&](const UnsupportedDisassembly &E) {
SuccessfullyInitialised = false;
outs()
<< "Note: CFIVerifyTests are disabled due to lack of x86 support "
<< "Note: CFIVerifyTests are disabled due to lack of support "
"on this build.\n";
});
}
}
bool SuccessfullyInitialised;
ELFx86TestFileAnalysis Analysis;
ELFTestFileAnalysis Analysis;
};
TEST_F(BasicFileAnalysisTest, BasicDisassemblyTraversalTest) {
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(
@ -190,7 +202,7 @@ TEST_F(BasicFileAnalysisTest, BasicDisassemblyTraversalTest) {
EXPECT_EQ(nullptr, Analysis.getPrevInstructionSequential(*InstrMeta));
}
TEST_F(BasicFileAnalysisTest, PrevAndNextFromBadInst) {
TEST_F(BasicX86FileAnalysisTest, PrevAndNextFromBadInst) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -213,7 +225,7 @@ TEST_F(BasicFileAnalysisTest, PrevAndNextFromBadInst) {
EXPECT_EQ(1u, GoodInstrMeta->InstructionSize);
}
TEST_F(BasicFileAnalysisTest, CFITrapTest) {
TEST_F(BasicX86FileAnalysisTest, CFITrapTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -248,7 +260,7 @@ TEST_F(BasicFileAnalysisTest, CFITrapTest) {
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 28)));
}
TEST_F(BasicFileAnalysisTest, FallThroughTest) {
TEST_F(BasicX86FileAnalysisTest, FallThroughTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -288,7 +300,7 @@ TEST_F(BasicFileAnalysisTest, FallThroughTest) {
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 19)));
}
TEST_F(BasicFileAnalysisTest, DefiniteNextInstructionTest) {
TEST_F(BasicX86FileAnalysisTest, DefiniteNextInstructionTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -378,7 +390,7 @@ TEST_F(BasicFileAnalysisTest, DefiniteNextInstructionTest) {
EXPECT_EQ(0xDEADBEEF + 4, Next->VMAddress);
}
TEST_F(BasicFileAnalysisTest, ControlFlowXRefsTest) {
TEST_F(BasicX86FileAnalysisTest, ControlFlowXRefsTest) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -483,7 +495,7 @@ TEST_F(BasicFileAnalysisTest, ControlFlowXRefsTest) {
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
}
TEST_F(BasicFileAnalysisTest, CFIProtectionInvalidTargets) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionInvalidTargets) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -507,7 +519,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionInvalidTargets) {
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicFileAnalysisTest, CFIProtectionBasicFallthroughToUd2) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionBasicFallthroughToUd2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -522,7 +534,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionBasicFallthroughToUd2) {
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicFileAnalysisTest, CFIProtectionBasicJumpToUd2) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionBasicJumpToUd2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -537,7 +549,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionBasicJumpToUd2) {
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathUd2) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualPathUd2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -555,7 +567,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathUd2) {
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathSingleUd2) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualPathSingleUd2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -572,7 +584,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathSingleUd2) {
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitUpwards) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualFailLimitUpwards) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -597,7 +609,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitUpwards) {
SearchLengthForConditionalBranch = PrevSearchLengthForConditionalBranch;
}
TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitDownwards) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualFailLimitDownwards) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -621,7 +633,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitDownwards) {
SearchLengthForUndef = PrevSearchLengthForUndef;
}
TEST_F(BasicFileAnalysisTest, CFIProtectionGoodAndBadPaths) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionGoodAndBadPaths) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -637,7 +649,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionGoodAndBadPaths) {
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicFileAnalysisTest, CFIProtectionWithUnconditionalJumpInFallthrough) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionWithUnconditionalJumpInFallthrough) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -653,7 +665,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionWithUnconditionalJumpInFallthrough) {
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicFileAnalysisTest, CFIProtectionComplexExample) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionComplexExample) {
if (!SuccessfullyInitialised)
return;
// See unittests/GraphBuilder.cpp::BuildFlowGraphComplexExample for this
@ -683,7 +695,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionComplexExample) {
SearchLengthForUndef = PrevSearchLengthForUndef;
}
TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTest) {
TEST_F(BasicX86FileAnalysisTest, UndefSearchLengthOneTest) {
Analysis.parseSectionContents(
{
0x77, 0x0d, // 0x688118: ja 0x688127 [+12]
@ -702,7 +714,7 @@ TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTest) {
SearchLengthForUndef = PrevSearchLengthForUndef;
}
TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTestFarAway) {
TEST_F(BasicX86FileAnalysisTest, UndefSearchLengthOneTestFarAway) {
Analysis.parseSectionContents(
{
0x74, 0x73, // 0x7759eb: je 0x775a60
@ -741,7 +753,7 @@ TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTestFarAway) {
SearchLengthForUndef = PrevSearchLengthForUndef;
}
TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathExplicit) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -757,7 +769,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit) {
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit2) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathExplicit2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -773,7 +785,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit2) {
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathImplicit) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathImplicit) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -789,7 +801,7 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathImplicit) {
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicFileAnalysisTest, CFIProtectionClobberDualPathImplicit) {
TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberDualPathImplicit) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
@ -807,6 +819,243 @@ TEST_F(BasicFileAnalysisTest, CFIProtectionClobberDualPathImplicit) {
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64BasicUnprotected) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x00, 0x01, 0x3f, 0xd6, // 0: blr x8
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF);
EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64BasicProtected) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
0x00, 0x01, 0x3f, 0xd6, // 8: blr x8
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 8);
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberBasic) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
0x08, 0x05, 0x00, 0x91, // 8: add x8, x8, #1
0x00, 0x01, 0x3f, 0xd6, // 12: blr x8
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 12);
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberOneLoad) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
0x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16]
0x20, 0x00, 0x1f, 0xd6, // 12: br x1
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 12);
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddGood) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
0x21, 0x04, 0x00, 0x91, // 8: add x1, x1, #1
0x21, 0x09, 0x40, 0xf9, // 12: ldr x1, [x9,#16]
0x20, 0x00, 0x1f, 0xd6, // 16: br x1
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16);
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddBad) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
0x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16]
0x21, 0x04, 0x00, 0x91, // 12: add x1, x1, #1
0x20, 0x00, 0x1f, 0xd6, // 16: br x1
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16);
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberLoadAddBad2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
0x29, 0x04, 0x00, 0x91, // 16: add x9, x1, #1
0x21, 0x09, 0x40, 0xf9, // 12: ldr x1, [x9,#16]
0x20, 0x00, 0x1f, 0xd6, // 16: br x1
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16);
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberTwoLoads) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
0x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16]
0x21, 0x08, 0x40, 0xf9, // 12: ldr x1, [x1,#16]
0x20, 0x00, 0x1f, 0xd6, // 16: br x1
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16);
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberUnrelatedSecondLoad) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
0x21, 0x09, 0x40, 0xf9, // 8: ldr x1, [x9,#16]
0x21, 0x09, 0x40, 0xf9, // 12: ldr x1, [x9,#16]
0x20, 0x00, 0x1f, 0xd6, // 16: br x1
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16);
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64ClobberUnrelatedLoads) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x49, 0x00, 0x00, 0x54, // 0: b.ls 8
0x20, 0x00, 0x20, 0xd4, // 4: brk #0x1
0x22, 0x09, 0x40, 0xf9, // 8: ldr x2, [x9,#16]
0x22, 0x08, 0x40, 0xf9, // 12: ldr x2, [x1,#16]
0x20, 0x00, 0x1f, 0xd6, // 16: br x1
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 16);
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64GoodAndBadPaths) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0x03, 0x00, 0x00, 0x14, // 0: b 12
0x49, 0x00, 0x00, 0x54, // 4: b.ls 8
0x20, 0x00, 0x20, 0xd4, // 8: brk #0x1
0x20, 0x00, 0x1f, 0xd6, // 12: br x1
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 12);
EXPECT_EQ(CFIProtectionStatus::FAIL_ORPHANS,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPaths) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0xc9, 0x00, 0x00, 0x54, // 0: b.ls 24
0x21, 0x08, 0x40, 0xf9, // 4: ldr x1, [x1,#16]
0x03, 0x00, 0x00, 0x14, // 8: b 12
0x69, 0x00, 0x00, 0x54, // 12: b.ls 12
0x21, 0x08, 0x40, 0xf9, // 16: ldr x1, [x1,#16]
0x20, 0x00, 0x1f, 0xd6, // 20: br x1
0x20, 0x00, 0x20, 0xd4, // 24: brk #0x1
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 20);
EXPECT_EQ(CFIProtectionStatus::PROTECTED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPathsBadLoad1) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0xe9, 0x00, 0x00, 0x54, // 0: b.ls 28
0x21, 0x08, 0x40, 0xf9, // 4: ldr x1, [x1,#16]
0x21, 0x08, 0x40, 0xf9, // 8: ldr x1, [x1,#16]
0x03, 0x00, 0x00, 0x14, // 12: b 12
0x69, 0x00, 0x00, 0x54, // 16: b.ls 12
0x21, 0x08, 0x40, 0xf9, // 20: ldr x1, [x1,#16]
0x20, 0x00, 0x1f, 0xd6, // 24: br x1
0x20, 0x00, 0x20, 0xd4, // 28: brk #0x1
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 24);
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
TEST_F(BasicAArch64FileAnalysisTest, AArch64TwoPathsBadLoad2) {
if (!SuccessfullyInitialised)
return;
Analysis.parseSectionContents(
{
0xe9, 0x00, 0x00, 0x54, // 0: b.ls 28
0x21, 0x08, 0x40, 0xf9, // 4: ldr x1, [x1,#16]
0x03, 0x00, 0x00, 0x14, // 8: b 12
0x89, 0x00, 0x00, 0x54, // 12: b.ls 16
0x21, 0x08, 0x40, 0xf9, // 16: ldr x1, [x1,#16]
0x21, 0x08, 0x40, 0xf9, // 20: ldr x1, [x1,#16]
0x20, 0x00, 0x1f, 0xd6, // 24: br x1
0x20, 0x00, 0x20, 0xd4, // 28: brk #0x1
},
0xDEADBEEF);
GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 24);
EXPECT_EQ(CFIProtectionStatus::FAIL_REGISTER_CLOBBERED,
Analysis.validateCFIProtection(Result));
}
} // anonymous namespace
} // end namespace cfi_verify
} // end namespace llvm

View File

@ -312,6 +312,7 @@ CodeGenInstruction::CodeGenInstruction(Record *R)
isBarrier = R->getValueAsBit("isBarrier");
isCall = R->getValueAsBit("isCall");
isAdd = R->getValueAsBit("isAdd");
isTrap = R->getValueAsBit("isTrap");
canFoldAsLoad = R->getValueAsBit("canFoldAsLoad");
isPredicable = Operands.isPredicable || R->getValueAsBit("isPredicable");
isConvertibleToThreeAddress = R->getValueAsBit("isConvertibleToThreeAddress");

View File

@ -232,6 +232,7 @@ template <typename T> class ArrayRef;
bool isBarrier : 1;
bool isCall : 1;
bool isAdd : 1;
bool isTrap : 1;
bool canFoldAsLoad : 1;
bool mayLoad : 1;
bool mayLoad_Unset : 1;

View File

@ -109,6 +109,7 @@ void EmitInstrDocs(RecordKeeper &RK, raw_ostream &OS) {
FLAG(isBarrier)
FLAG(isCall)
FLAG(isAdd)
FLAG(isTrap)
FLAG(canFoldAsLoad)
FLAG(mayLoad)
//FLAG(mayLoad_Unset) // Deliberately omitted.

View File

@ -576,6 +576,7 @@ void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num,
if (Inst.isMoveReg) OS << "|(1ULL<<MCID::MoveReg)";
if (Inst.isBitcast) OS << "|(1ULL<<MCID::Bitcast)";
if (Inst.isAdd) OS << "|(1ULL<<MCID::Add)";
if (Inst.isTrap) OS << "|(1ULL<<MCID::Trap)";
if (Inst.isSelect) OS << "|(1ULL<<MCID::Select)";
if (Inst.isBarrier) OS << "|(1ULL<<MCID::Barrier)";
if (Inst.hasDelaySlot) OS << "|(1ULL<<MCID::DelaySlot)";