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_common.cc b/runtime/interpreter/interpreter_common.cc
index 86a6aea..36b250c 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -22,6 +22,7 @@
 template<InvokeType type, bool is_range, bool do_access_check>
 bool DoInvoke(Thread* self, ShadowFrame& shadow_frame,
               const Instruction* inst, JValue* result) {
+  bool do_assignability_check = do_access_check;
   uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
   uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
   Object* receiver = (type == kStatic) ? NULL : shadow_frame.GetVRegReference(vregC);
@@ -61,6 +62,10 @@
     ++cur_reg;
   }
 
+  const DexFile::TypeList* params;
+  if (do_assignability_check) {
+    params = mh.GetParameterTypeList();
+  }
   size_t arg_offset = (receiver == NULL) ? 0 : 1;
   const char* shorty = mh.GetShorty();
   uint32_t arg[5];
@@ -73,6 +78,23 @@
     switch (shorty[shorty_pos + 1]) {
       case 'L': {
         Object* o = shadow_frame.GetVRegReference(arg_pos);
+        if (do_assignability_check && o != NULL) {
+          Class* arg_type = mh.GetClassFromTypeIdx(params->GetTypeItem(shorty_pos).type_idx_);
+          if (arg_type == NULL) {
+            CHECK(self->IsExceptionPending());
+            return false;
+          }
+          if (!o->VerifierInstanceOf(arg_type)) {
+            // This should never happen.
+            self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+                                     "Ljava/lang/VirtualMachineError;",
+                                     "Invoking %s with bad arg %d, type '%s' not instance of '%s'",
+                                     mh.GetName(), shorty_pos,
+                                     ClassHelper(o->GetClass()).GetDescriptor(),
+                                     ClassHelper(arg_type).GetDescriptor());
+            return false;
+          }
+        }
         new_shadow_frame->SetVRegReference(cur_reg, o);
         break;
       }
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index a6c5e62..3ad1935 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -215,6 +215,7 @@
 template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
 static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
                               const Instruction* inst, uint16_t inst_data) {
+  bool do_assignability_check = do_access_check;
   bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
   uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
   ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self,
@@ -255,9 +256,24 @@
     case Primitive::kPrimLong:
       f->SetLong(obj, shadow_frame.GetVRegLong(vregA));
       break;
-    case Primitive::kPrimNot:
-      f->SetObj(obj, shadow_frame.GetVRegReference(vregA));
+    case Primitive::kPrimNot: {
+      Object* reg = shadow_frame.GetVRegReference(vregA);
+      if (do_assignability_check && reg != NULL) {
+        Class* field_class = FieldHelper(f).GetType();
+        if (!reg->VerifierInstanceOf(field_class)) {
+          // This should never happen.
+          self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+                                   "Ljava/lang/VirtualMachineError;",
+                                   "Put '%s' that is not instance of field '%s' in '%s'",
+                                   ClassHelper(reg->GetClass()).GetDescriptor(),
+                                   ClassHelper(field_class).GetDescriptor(),
+                                   ClassHelper(f->GetDeclaringClass()).GetDescriptor());
+          return false;
+        }
+      }
+      f->SetObj(obj, reg);
       break;
+    }
     default:
       LOG(FATAL) << "Unreachable: " << field_type;
   }
@@ -479,7 +495,8 @@
 }
 
 static inline void TraceExecution(const ShadowFrame& shadow_frame, const Instruction* inst,
-                                  const uint32_t dex_pc, MethodHelper& mh) {
+                                  const uint32_t dex_pc, MethodHelper& mh)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   const bool kTracing = false;
   if (kTracing) {
 #define TRACE_LOG std::cerr
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index d70b80e..018add3 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -74,6 +74,7 @@
 template<bool do_access_check>
 JValue ExecuteGotoImpl(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();
@@ -264,11 +265,28 @@
 
   HANDLE_INSTRUCTION_START(RETURN_OBJECT) {
     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(), dex_pc,
@@ -512,6 +530,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());
     }
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());
         }