Index: include/llvm/MC/MCInstrDesc.h =================================================================== --- include/llvm/MC/MCInstrDesc.h +++ include/llvm/MC/MCInstrDesc.h @@ -149,7 +149,8 @@ ExtractSubreg, InsertSubreg, Convergent, - Add + Add, + Trap }; } @@ -245,6 +246,9 @@ /// 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); } Index: include/llvm/Target/Target.td =================================================================== --- include/llvm/Target/Target.td +++ include/llvm/Target/Target.td @@ -448,6 +448,7 @@ 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? Index: lib/Target/AArch64/AArch64InstrInfo.td =================================================================== --- lib/Target/AArch64/AArch64InstrInfo.td +++ lib/Target/AArch64/AArch64InstrInfo.td @@ -1442,7 +1442,9 @@ //===----------------------------------------------------------------------===// // 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">; Index: lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp =================================================================== --- lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp +++ lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp @@ -140,14 +140,18 @@ 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; } }; Index: lib/Target/X86/X86InstrSystem.td =================================================================== --- lib/Target/X86/X86InstrSystem.td +++ lib/Target/X86/X86InstrSystem.td @@ -22,7 +22,7 @@ // 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; } Index: test/tools/llvm-cfi-verify/AArch64/protected-lineinfo.s =================================================================== --- /dev/null +++ test/tools/llvm-cfi-verify/AArch64/protected-lineinfo.s @@ -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: Index: test/tools/llvm-cfi-verify/AArch64/unprotected-lineinfo.s =================================================================== --- /dev/null +++ test/tools/llvm-cfi-verify/AArch64/unprotected-lineinfo.s @@ -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: Index: tools/llvm-cfi-verify/lib/FileAnalysis.h =================================================================== --- tools/llvm-cfi-verify/lib/FileAnalysis.h +++ tools/llvm-cfi-verify/lib/FileAnalysis.h @@ -149,10 +149,13 @@ 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- Index: tools/llvm-cfi-verify/lib/FileAnalysis.cpp =================================================================== --- tools/llvm-cfi-verify/lib/FileAnalysis.cpp +++ tools/llvm-cfi-verify/lib/FileAnalysis.cpp @@ -154,7 +154,8 @@ } 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 @@ 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 CurRegisterNumbers = RegisterNumbers; + std::vector 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 @@ if (!usesRegisterOperand(InstrMeta)) continue; + if (InstrDesc.isReturn()) + continue; + // Check if this instruction exists in the range of the DWARF metadata. if (!IgnoreDWARFFlag) { auto LineInfo = Index: unittests/tools/llvm-cfi-verify/FileAnalysis.cpp =================================================================== --- unittests/tools/llvm-cfi-verify/FileAnalysis.cpp +++ unittests/tools/llvm-cfi-verify/FileAnalysis.cpp @@ -45,10 +45,10 @@ 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 SectionBytes, @@ -62,6 +62,8 @@ }; class BasicFileAnalysisTest : public ::testing::Test { +public: + BasicFileAnalysisTest(StringRef Trip) : Analysis(Trip) {} protected: virtual void SetUp() { IgnoreDWARFFlag = true; @@ -70,17 +72,27 @@ 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; +}; + +class BasicX86FileAnalysisTest : public BasicFileAnalysisTest { +public: + BasicX86FileAnalysisTest() : BasicFileAnalysisTest("x86_64--") {} +}; + +class BasicAArch64FileAnalysisTest : public BasicFileAnalysisTest { +public: + BasicAArch64FileAnalysisTest() : BasicFileAnalysisTest("aarch64--") {} }; -TEST_F(BasicFileAnalysisTest, BasicDisassemblyTraversalTest) { +TEST_F(BasicX86FileAnalysisTest, BasicDisassemblyTraversalTest) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -190,7 +202,7 @@ EXPECT_EQ(nullptr, Analysis.getPrevInstructionSequential(*InstrMeta)); } -TEST_F(BasicFileAnalysisTest, PrevAndNextFromBadInst) { +TEST_F(BasicX86FileAnalysisTest, PrevAndNextFromBadInst) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -213,7 +225,7 @@ EXPECT_EQ(1u, GoodInstrMeta->InstructionSize); } -TEST_F(BasicFileAnalysisTest, CFITrapTest) { +TEST_F(BasicX86FileAnalysisTest, CFITrapTest) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -248,7 +260,7 @@ Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 28))); } -TEST_F(BasicFileAnalysisTest, FallThroughTest) { +TEST_F(BasicX86FileAnalysisTest, FallThroughTest) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -288,7 +300,7 @@ Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 19))); } -TEST_F(BasicFileAnalysisTest, DefiniteNextInstructionTest) { +TEST_F(BasicX86FileAnalysisTest, DefiniteNextInstructionTest) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -378,7 +390,7 @@ EXPECT_EQ(0xDEADBEEF + 4, Next->VMAddress); } -TEST_F(BasicFileAnalysisTest, ControlFlowXRefsTest) { +TEST_F(BasicX86FileAnalysisTest, ControlFlowXRefsTest) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -483,7 +495,7 @@ EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty()); } -TEST_F(BasicFileAnalysisTest, CFIProtectionInvalidTargets) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionInvalidTargets) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -507,7 +519,7 @@ Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionBasicFallthroughToUd2) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionBasicFallthroughToUd2) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -522,7 +534,7 @@ Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionBasicJumpToUd2) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionBasicJumpToUd2) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -537,7 +549,7 @@ Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathUd2) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualPathUd2) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -555,7 +567,7 @@ Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionDualPathSingleUd2) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualPathSingleUd2) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -572,7 +584,7 @@ Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitUpwards) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualFailLimitUpwards) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -597,7 +609,7 @@ SearchLengthForConditionalBranch = PrevSearchLengthForConditionalBranch; } -TEST_F(BasicFileAnalysisTest, CFIProtectionDualFailLimitDownwards) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionDualFailLimitDownwards) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -621,7 +633,7 @@ SearchLengthForUndef = PrevSearchLengthForUndef; } -TEST_F(BasicFileAnalysisTest, CFIProtectionGoodAndBadPaths) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionGoodAndBadPaths) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -637,7 +649,7 @@ Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionWithUnconditionalJumpInFallthrough) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionWithUnconditionalJumpInFallthrough) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -653,7 +665,7 @@ 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 @@ SearchLengthForUndef = PrevSearchLengthForUndef; } -TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTest) { +TEST_F(BasicX86FileAnalysisTest, UndefSearchLengthOneTest) { Analysis.parseSectionContents( { 0x77, 0x0d, // 0x688118: ja 0x688127 [+12] @@ -702,7 +714,7 @@ SearchLengthForUndef = PrevSearchLengthForUndef; } -TEST_F(BasicFileAnalysisTest, UndefSearchLengthOneTestFarAway) { +TEST_F(BasicX86FileAnalysisTest, UndefSearchLengthOneTestFarAway) { Analysis.parseSectionContents( { 0x74, 0x73, // 0x7759eb: je 0x775a60 @@ -741,7 +753,7 @@ SearchLengthForUndef = PrevSearchLengthForUndef; } -TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathExplicit) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -757,7 +769,7 @@ Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathExplicit2) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathExplicit2) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -773,7 +785,7 @@ Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionClobberSinglePathImplicit) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberSinglePathImplicit) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -789,7 +801,7 @@ Analysis.validateCFIProtection(Result)); } -TEST_F(BasicFileAnalysisTest, CFIProtectionClobberDualPathImplicit) { +TEST_F(BasicX86FileAnalysisTest, CFIProtectionClobberDualPathImplicit) { if (!SuccessfullyInitialised) return; Analysis.parseSectionContents( @@ -807,6 +819,243 @@ 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 Index: utils/TableGen/CodeGenInstruction.h =================================================================== --- utils/TableGen/CodeGenInstruction.h +++ utils/TableGen/CodeGenInstruction.h @@ -232,6 +232,7 @@ bool isBarrier : 1; bool isCall : 1; bool isAdd : 1; + bool isTrap : 1; bool canFoldAsLoad : 1; bool mayLoad : 1; bool mayLoad_Unset : 1; Index: utils/TableGen/CodeGenInstruction.cpp =================================================================== --- utils/TableGen/CodeGenInstruction.cpp +++ utils/TableGen/CodeGenInstruction.cpp @@ -312,6 +312,7 @@ 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"); Index: utils/TableGen/InstrDocsEmitter.cpp =================================================================== --- utils/TableGen/InstrDocsEmitter.cpp +++ utils/TableGen/InstrDocsEmitter.cpp @@ -109,6 +109,7 @@ FLAG(isBarrier) FLAG(isCall) FLAG(isAdd) + FLAG(isTrap) FLAG(canFoldAsLoad) FLAG(mayLoad) //FLAG(mayLoad_Unset) // Deliberately omitted. Index: utils/TableGen/InstrInfoEmitter.cpp =================================================================== --- utils/TableGen/InstrInfoEmitter.cpp +++ utils/TableGen/InstrInfoEmitter.cpp @@ -576,6 +576,7 @@ if (Inst.isMoveReg) OS << "|(1ULL<