ART: Reject branch/switch/throw to move-result.

move-result* instructions must occur after invoke instructions,
however it was still possible to branch or switch to a move-result*
instruction with creative DEX manipulation. The verifier now rejects
this situation, as well as having a move-result* instruction as the
first instruction in a try block's handler. This now ensures that
move-result* must happen dynamically after an invoke, not just
statically.

Change-Id: Ida97852f4051310fdaf38bed1d6e1c5a541c85c5
Signed-off-by: Stephen Kyle <stephen.kyle@arm.com>
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index f28d488..45e8b6a 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -650,6 +650,11 @@
             << "exception handler starts at bad address (" << dex_pc << ")";
         return false;
       }
+      if (!CheckNotMoveResult(code_item_->insns_, dex_pc)) {
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+            << "exception handler begins with move-result* (" << dex_pc << ")";
+        return false;
+      }
       insn_flags_[dex_pc].SetBranchTarget();
       // Ensure exception types are resolved so that they don't need resolution to be delivered,
       // unresolved exception types will be ignored by exception delivery
@@ -2766,7 +2771,7 @@
       return false;
     }
     DCHECK_EQ(isConditional, (opcode_flags & Instruction::kContinue) != 0);
-    if (!CheckNotMoveException(code_item_->insns_, work_insn_idx_ + branch_target)) {
+    if (!CheckNotMoveExceptionOrMoveResult(code_item_->insns_, work_insn_idx_ + branch_target)) {
       return false;
     }
     /* update branch target, set "changed" if appropriate */
@@ -2812,7 +2817,7 @@
          (((int32_t) switch_insns[offset_to_targets + targ * 2 + 1]) << 16);
       abs_offset = work_insn_idx_ + offset;
       DCHECK_LT(abs_offset, code_item_->insns_size_in_code_units_);
-      if (!CheckNotMoveException(code_item_->insns_, abs_offset)) {
+      if (!CheckNotMoveExceptionOrMoveResult(code_item_->insns_, abs_offset)) {
         return false;
       }
       if (!UpdateRegisters(abs_offset, work_line_.get(), false)) {
@@ -4001,6 +4006,19 @@
   return true;
 }
 
+bool MethodVerifier::CheckNotMoveResult(const uint16_t* insns, int insn_idx) {
+  if (((insns[insn_idx] & 0xff) >= Instruction::MOVE_RESULT) &&
+      ((insns[insn_idx] & 0xff) <= Instruction::MOVE_RESULT_OBJECT)) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid use of move-result*";
+    return false;
+  }
+  return true;
+}
+
+bool MethodVerifier::CheckNotMoveExceptionOrMoveResult(const uint16_t* insns, int insn_idx) {
+  return (CheckNotMoveException(insns, insn_idx) && CheckNotMoveResult(insns, insn_idx));
+}
+
 bool MethodVerifier::UpdateRegisters(uint32_t next_insn, RegisterLine* merge_line,
                                      bool update_merge_line) {
   bool changed = true;
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 87acb20..9f5efe8 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -612,6 +612,21 @@
   bool CheckNotMoveException(const uint16_t* insns, int insn_idx);
 
   /*
+   * Verify that the target instruction is not "move-result". It is important that we cannot
+   * branch to move-result instructions, but we have to make this a distinct check instead of
+   * adding it to CheckNotMoveException, because it is legal to continue into "move-result"
+   * instructions - as long as the previous instruction was an invoke, which is checked elsewhere.
+   */
+  bool CheckNotMoveResult(const uint16_t* insns, int insn_idx);
+
+  /*
+   * Verify that the target instruction is not "move-result" or "move-exception". This is to
+   * be used when checking branch and switch instructions, but not instructions that can
+   * continue.
+   */
+  bool CheckNotMoveExceptionOrMoveResult(const uint16_t* insns, int insn_idx);
+
+  /*
   * Control can transfer to "next_insn". Merge the registers from merge_line into the table at
   * next_insn, and set the changed flag on the target address if any of the registers were changed.
   * In the case of fall-through, update the merge line on a change as its the working line for the