Fix handling of unresolved references in verifier.

The verifier should not treat use of unresolved references as a reason to reject
the entire class. Instead, the verifier treats the instruction as a throw. If
that class is run, the interpreter with extra checks will throw an exception.

Bug: 10457426

Change-Id: I3799da843a7ffb3519bbf6dc13a6276519d9cb95
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index d49807c..2d658b3 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -53,6 +53,7 @@
 template<bool do_access_check>
 static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
                                 ShadowFrame& shadow_frame, JValue result_register) {
+  bool do_assignability_check = do_access_check;
   if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
     LOG(FATAL) << "Invalid shadow frame for interpreter use";
     return JValue();
@@ -226,11 +227,28 @@
       case Instruction::RETURN_OBJECT: {
         PREAMBLE();
         JValue result;
+        Object* obj_result = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
         result.SetJ(0);
-        result.SetL(shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)));
+        result.SetL(obj_result);
         if (UNLIKELY(self->TestAllFlags())) {
           CheckSuspend(self);
         }
+        if (do_assignability_check && obj_result != NULL) {
+          Class* return_type = MethodHelper(shadow_frame.GetMethod()).GetReturnType();
+          if (return_type == NULL) {
+            // Return the pending exception.
+            HANDLE_PENDING_EXCEPTION();
+          }
+          if (!obj_result->VerifierInstanceOf(return_type)) {
+            // This should never happen.
+            self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+                                     "Ljava/lang/VirtualMachineError;",
+                                     "Returning '%s' that is not instance of return type '%s'",
+                                     ClassHelper(obj_result->GetClass()).GetDescriptor(),
+                                     ClassHelper(return_type).GetDescriptor());
+            HANDLE_PENDING_EXCEPTION();
+          }
+        }
         if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
           instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                            shadow_frame.GetMethod(), inst->GetDexPc(insns),
@@ -472,6 +490,12 @@
         Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
         if (UNLIKELY(exception == NULL)) {
           ThrowNullPointerException(NULL, "throw with null exception");
+        } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
+          // This should never happen.
+          self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+                                   "Ljava/lang/VirtualMachineError;",
+                                   "Throwing '%s' that is not instance of Throwable",
+                                   ClassHelper(exception->GetClass()).GetDescriptor());
         } else {
           self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable());
         }