[cfi-verify] Validate there are no register clobbers between CFI-check and instruction execution.
Summary:
This patch adds another failure mode for `validateCFIProtection(..)`, wherein any register that affects the indirect control flow instruction is clobbered to between the CFI-check and the instruction's execution.
Also includes a modification to make MCInstrDesc::hasDefOfPhysReg public.
Reviewers: vlad.tsyrklevich
Reviewed By: vlad.tsyrklevich
Subscribers: llvm-commits, pcc, kcc
Differential Revision: https://reviews.llvm.org/D39820
llvm-svn: 318238
diff --git a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
index 8f3a683..464454c 100644
--- a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
+++ b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
@@ -64,6 +64,8 @@
return "FAIL_ORPHANS";
case CFIProtectionStatus::FAIL_BAD_CONDITIONAL_BRANCH:
return "FAIL_BAD_CONDITIONAL_BRANCH";
+ case CFIProtectionStatus::FAIL_REGISTER_CLOBBERED:
+ return "FAIL_REGISTER_CLOBBERED";
case CFIProtectionStatus::FAIL_INVALID_INSTRUCTION:
return "FAIL_INVALID_INSTRUCTION";
}
@@ -270,9 +272,52 @@
return CFIProtectionStatus::FAIL_BAD_CONDITIONAL_BRANCH;
}
+ if (indirectCFOperandClobber(Graph) != Graph.BaseAddress)
+ return CFIProtectionStatus::FAIL_REGISTER_CLOBBERED;
+
return CFIProtectionStatus::PROTECTED;
}
+uint64_t FileAnalysis::indirectCFOperandClobber(const GraphResult &Graph) const {
+ assert(Graph.OrphanedNodes.empty() && "Orphaned nodes should be empty.");
+
+ // Get the set of registers we must check to ensure they're not clobbered.
+ const Instr &IndirectCF = getInstructionOrDie(Graph.BaseAddress);
+ DenseSet<unsigned> 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 clobbering 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.hasDefOfPhysReg(NodeInstr.Instruction, RegNum,
+ *RegisterInfo))
+ 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());
diff --git a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h
index 820c368..ce81f8b 100644
--- a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h
+++ b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h
@@ -59,6 +59,9 @@
// There is a path to the instruction from a conditional branch that does not
// properly check the destination for this vcall/icall.
FAIL_BAD_CONDITIONAL_BRANCH,
+ // One of the operands of the indirect CF instruction is modified between the
+ // CFI-check and execution.
+ FAIL_REGISTER_CLOBBERED,
// The instruction referenced does not exist. This normally indicates an
// error in the program, where you try and validate a graph that was created
// in a different FileAnalysis object.
@@ -145,6 +148,13 @@
// flow instruction in this file.
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).
+ uint64_t indirectCFOperandClobber(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;
diff --git a/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp b/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
index ff92171..65d4b99 100644
--- a/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
+++ b/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
@@ -301,6 +301,7 @@
BranchNode.Target = 0;
BranchNode.Fallthrough = 0;
BranchNode.CFIProtection = false;
+ BranchNode.IndirectCFIsOnTargetPath = (BranchTarget == Address);
if (BranchTarget == Address)
BranchNode.Target = Address;
diff --git a/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.h b/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.h
index d1ce509..2bf07b5 100644
--- a/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.h
+++ b/llvm/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