Index: tools/llvm-cfi-verify/lib/FileAnalysis.h =================================================================== --- tools/llvm-cfi-verify/lib/FileAnalysis.h +++ tools/llvm-cfi-verify/lib/FileAnalysis.h @@ -56,6 +56,8 @@ FAIL_BAD_CONDITIONAL_BRANCH, // There is a path to the instruction from a // conditional branch that does not properly // check the destination for this vcall/icall. + FAIL_REGISTER_SPILLED, // One of the operands of the indirect CF instruction + // is modified between the CFI-check and execution. FAIL_UNKNOWN, // Something strange happened, e.g. the instruction was not in // this file. }; @@ -140,6 +142,13 @@ // flow instruction in this file. CFIProtectionStatus validateCFIProtection(const GraphResult &Graph) const; + // Returns the first place the operand register spills between the CFI-check + // and the indirect CF instruction execution. If the register does not spill, + // returns the address of the indirect CF instruction. The result is undefined + // if the provided graph does not fall under either the FAIL_REGISTER_SPILLED + // or PROTECTED status (see CFIProtectionStatus). + uint64_t indirectCFOperandSpill(const GraphResult& Graph) const; + // Prints an instruction to the provided stream using this object's pretty- // printers. void printInstruction(const Instr &InstrMeta, raw_ostream &OS) const; Index: tools/llvm-cfi-verify/lib/FileAnalysis.cpp =================================================================== --- tools/llvm-cfi-verify/lib/FileAnalysis.cpp +++ tools/llvm-cfi-verify/lib/FileAnalysis.cpp @@ -64,6 +64,8 @@ return "FAIL_ORPHANS"; case FAIL_BAD_CONDITIONAL_BRANCH: return "FAIL_BAD_CONDITIONAL_BRANCH"; + case FAIL_REGISTER_SPILLED: + return "FAIL_REGISTER_SPILLED"; case FAIL_UNKNOWN: return "FAIL_UNKNOWN"; } @@ -270,9 +272,51 @@ return FAIL_BAD_CONDITIONAL_BRANCH; } + if (indirectCFOperandSpill(Graph) != Graph.BaseAddress) + return FAIL_REGISTER_SPILLED; + return PROTECTED; } +uint64_t FileAnalysis::indirectCFOperandSpill(const GraphResult &Graph) const { + assert(Graph.OrphanedNodes.empty() && "Orphaned nodes should be empty."); + + // Get the set of registers we must spill check. + const Instr &IndirectCF = getInstructionOrDie(Graph.BaseAddress); + DenseSet RegisterNumbers; + for (const auto &Operand : IndirectCF.Instruction) { + if (Operand.isReg()) + RegisterNumbers.insert(Operand.getReg()); + } + assert(RegisterNumbers.size() && "Zero register operands on indirect CF."); + + // Now check all branches to indirect CFs and ensure no spill happens. + for (const auto Branch : Graph.ConditionalBranchNodes) { + uint64_t Node; + if (Branch.IndirectCFIsOnTargetPath) + Node = Branch.Target; + else + Node = Branch.Fallthrough; + + while (Node != Graph.BaseAddress) { + const Instr &NodeInstr = getInstructionOrDie(Node); + const auto &InstrDesc = MII->get(NodeInstr.Instruction.getOpcode()); + + for (unsigned RegNum : RegisterNumbers) { + if (InstrDesc.hasImplicitDefOfPhysReg(RegNum, RegisterInfo.get())) + return Node; + } + + const auto &KV = Graph.IntermediateNodes.find(Node); + assert((KV != Graph.IntermediateNodes.end()) && + "Could not get next node."); + Node = KV->second; + } + } + + return Graph.BaseAddress; +} + void FileAnalysis::printInstruction(const Instr &InstrMeta, raw_ostream &OS) const { Printer->printInst(&InstrMeta.Instruction, OS, "", *SubtargetInfo.get()); Index: tools/llvm-cfi-verify/lib/GraphBuilder.h =================================================================== --- tools/llvm-cfi-verify/lib/GraphBuilder.h +++ tools/llvm-cfi-verify/lib/GraphBuilder.h @@ -59,6 +59,7 @@ // is a CFI trap, and... // - The exit point of the other basic block is an undirect CF instruction. bool CFIProtection; + bool IndirectCFIsOnTargetPath; }; // The canonical graph result structure returned by GraphBuilder. The members Index: tools/llvm-cfi-verify/lib/GraphBuilder.cpp =================================================================== --- tools/llvm-cfi-verify/lib/GraphBuilder.cpp +++ tools/llvm-cfi-verify/lib/GraphBuilder.cpp @@ -301,10 +301,13 @@ BranchNode.Fallthrough = 0; BranchNode.CFIProtection = false; - if (BranchTarget == Address) + if (BranchTarget == Address) { BranchNode.Target = Address; - else + BranchNode.IndirectCFIsOnTargetPath = true; + } else { BranchNode.Fallthrough = Address; + BranchNode.IndirectCFIsOnTargetPath = false; + } HasValidCrossRef = true; buildFlowsToUndefined(Analysis, Result, BranchNode, ParentMeta); Index: unittests/tools/llvm-cfi-verify/FileAnalysis.cpp =================================================================== --- unittests/tools/llvm-cfi-verify/FileAnalysis.cpp +++ unittests/tools/llvm-cfi-verify/FileAnalysis.cpp @@ -724,6 +724,38 @@ SearchLengthForUndef = PrevSearchLengthForUndef; } +TEST_F(BasicFileAnalysisTest, CFIProtectionSpillSinglePath) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0x75, 0x02, // 0: jne 4 [+2] + 0x0f, 0x0b, // 2: ud2 + 0x05, 0x00, 0x00, 0x00, 0x00, // 4: add $0x0, %eax + 0xff, 0x10, // 9: callq *(%rax) + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 9); + EXPECT_EQ(FAIL_REGISTER_SPILLED, Analysis.validateCFIProtection(Result)); +} + +TEST_F(BasicFileAnalysisTest, CFIProtectionSpillDualPath) { + if (!SuccessfullyInitialised) + return; + Analysis.parseSectionContents( + { + 0x75, 0x04, // 0: jne 6 [+4] + 0x0f, 0x31, // 2: rdtsc (note: affects eax) + 0xff, 0x10, // 4: callq *(%rax) + 0x0f, 0x0b, // 6: ud2 + 0x75, 0xf9, // 8: jne 2 [-7] + 0x0f, 0x0b, // 10: ud2 + }, + 0xDEADBEEF); + GraphResult Result = GraphBuilder::buildFlowGraph(Analysis, 0xDEADBEEF + 4); + EXPECT_EQ(FAIL_REGISTER_SPILLED, Analysis.validateCFIProtection(Result)); +} + } // anonymous namespace } // end namespace cfi_verify } // end namespace llvm