[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