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:
parent
2705c2710a
commit
9249622410
@ -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); }
|
||||
|
||||
|
@ -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?
|
||||
|
@ -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">;
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
183
test/tools/llvm-cfi-verify/AArch64/protected-lineinfo.s
Normal file
183
test/tools/llvm-cfi-verify/AArch64/protected-lineinfo.s
Normal 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:
|
169
test/tools/llvm-cfi-verify/AArch64/unprotected-lineinfo.s
Normal file
169
test/tools/llvm-cfi-verify/AArch64/unprotected-lineinfo.s
Normal 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:
|
@ -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 =
|
||||
|
@ -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-
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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)";
|
||||
|
Loading…
Reference in New Issue
Block a user