Verify jmethodID when calling methods.

The JNI Call<type>Method, CallNonvirtual<type>Method, and
CallStatic<type>Method functions take an object (or class) and a
jmethodID as arguments.  For virtual calls, if the jmethodID is not
for a method in the provided object, the VM can crash.  (The most
common way to accomplish this is to pass a class object into a virtual
method call.)

For static method calls, the method defines the class, and we ignore
the jclass argument.  Still, we don't want badly-formed code floating
around, so we just log a warning.

This is part of CheckJNI.

Bug 2577881.

Change-Id: Ia19f0cb604b06a6bd4da8e3d6238d4e162b1480e
diff --git a/vm/CheckJni.c b/vm/CheckJni.c
index f4c761e..ae98c4c 100644
--- a/vm/CheckJni.c
+++ b/vm/CheckJni.c
@@ -251,6 +251,10 @@
     checkNonNull(_env, _ptr, __FUNCTION__)
 #define CHECK_SIG(_env, _methid, _sigbyte, _isstatic)                       \
     checkSig(_env, _methid, _sigbyte, _isstatic, __FUNCTION__)
+#define CHECK_VIRTUAL_METHOD(_env, _obj, _methid)                           \
+    checkVirtualMethod(_env, _obj, _methid, __FUNCTION__)
+#define CHECK_STATIC_METHOD(_env, _clazz, _methid)                          \
+    checkStaticMethod(_env, _clazz, _methid, __FUNCTION__)
 #define CHECK_METHOD_ARGS_A(_env, _methid, _args)                           \
     checkMethodArgsA(_env, _methid, _args, __FUNCTION__)
 #define CHECK_METHOD_ARGS_V(_env, _methid, _args)                           \
@@ -858,6 +862,56 @@
 }
 
 /*
+ * Verify that "methodID" is appropriate for "jobj".
+ *
+ * Make sure the object is an instance of the method's declaring class.
+ * (Note the methodID might point to a declaration in an interface; this
+ * will be handled automatically by the instanceof check.)
+ */
+static void checkVirtualMethod(JNIEnv* env, jobject jobj, jmethodID methodID,
+    const char* func)
+{
+    JNI_ENTER();
+
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    const Method* meth = (const Method*) methodID;
+
+    if (!dvmInstanceof(obj->clazz, meth->clazz)) {
+        LOGW("JNI WARNING: can't call %s.%s on instance of %s\n",
+            meth->clazz->descriptor, meth->name, obj->clazz->descriptor);
+        abortMaybe();
+    }
+
+    JNI_EXIT();
+}
+
+/*
+ * Verify that "methodID" is appropriate for "clazz".
+ *
+ * A mismatch isn't dangerous, because the method defines the class.  In
+ * fact, jclazz is unused in the implementation.  It's best if we don't
+ * allow bad code in the system though.
+ *
+ * Instances of "jclazz" must be instances of the method's declaring class.
+ */
+static void checkStaticMethod(JNIEnv* env, jclass jclazz, jmethodID methodID,
+    const char* func)
+{
+    JNI_ENTER();
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    const Method* meth = (const Method*) methodID;
+
+    if (!dvmInstanceof(clazz, meth->clazz)) {
+        LOGW("JNI WARNING: can't call static %s.%s on class %s\n",
+            meth->clazz->descriptor, meth->name, clazz->descriptor);
+        // no abort
+    }
+
+    JNI_EXIT();
+}
+
+/*
  * Verify that the reference arguments being passed in are appropriate for
  * this method.
  *
@@ -1697,6 +1751,7 @@
         CHECK_ENTER(env, kFlag_Default);                                    \
         CHECK_OBJECT(env, obj);                                             \
         CHECK_SIG(env, methodID, _retsig, false);                           \
+        CHECK_VIRTUAL_METHOD(env, obj, methodID);                           \
         _retdecl;                                                           \
         va_list args, tmpArgs;                                              \
         va_start(args, methodID);                                           \
@@ -1715,6 +1770,7 @@
         CHECK_ENTER(env, kFlag_Default);                                    \
         CHECK_OBJECT(env, obj);                                             \
         CHECK_SIG(env, methodID, _retsig, false);                           \
+        CHECK_VIRTUAL_METHOD(env, obj, methodID);                           \
         _retdecl;                                                           \
         va_list tmpArgs;                                                    \
         va_copy(tmpArgs, args);                                             \
@@ -1731,6 +1787,7 @@
         CHECK_ENTER(env, kFlag_Default);                                    \
         CHECK_OBJECT(env, obj);                                             \
         CHECK_SIG(env, methodID, _retsig, false);                           \
+        CHECK_VIRTUAL_METHOD(env, obj, methodID);                           \
         _retdecl;                                                           \
         CHECK_METHOD_ARGS_A(env, methodID, args);                           \
         _retasgn BASE_ENV(env)->Call##_jname##MethodA(env, obj, methodID,   \
@@ -1758,6 +1815,7 @@
         CHECK_CLASS(env, clazz);                                            \
         CHECK_OBJECT(env, obj);                                             \
         CHECK_SIG(env, methodID, _retsig, false);                           \
+        CHECK_VIRTUAL_METHOD(env, obj, methodID);                           \
         _retdecl;                                                           \
         va_list args, tmpArgs;                                              \
         va_start(args, methodID);                                           \
@@ -1777,6 +1835,7 @@
         CHECK_CLASS(env, clazz);                                            \
         CHECK_OBJECT(env, obj);                                             \
         CHECK_SIG(env, methodID, _retsig, false);                           \
+        CHECK_VIRTUAL_METHOD(env, obj, methodID);                           \
         _retdecl;                                                           \
         va_list tmpArgs;                                                    \
         va_copy(tmpArgs, args);                                             \
@@ -1794,6 +1853,7 @@
         CHECK_CLASS(env, clazz);                                            \
         CHECK_OBJECT(env, obj);                                             \
         CHECK_SIG(env, methodID, _retsig, false);                           \
+        CHECK_VIRTUAL_METHOD(env, obj, methodID);                           \
         _retdecl;                                                           \
         CHECK_METHOD_ARGS_A(env, methodID, args);                           \
         _retasgn BASE_ENV(env)->CallNonvirtual##_jname##MethodA(env, obj,   \
@@ -1820,6 +1880,7 @@
         CHECK_ENTER(env, kFlag_Default);                                    \
         CHECK_CLASS(env, clazz);                                            \
         CHECK_SIG(env, methodID, _retsig, true);                            \
+        CHECK_STATIC_METHOD(env, clazz, methodID);                          \
         _retdecl;                                                           \
         va_list args, tmpArgs;                                              \
         va_start(args, methodID);                                           \
@@ -1838,6 +1899,7 @@
         CHECK_ENTER(env, kFlag_Default);                                    \
         CHECK_CLASS(env, clazz);                                            \
         CHECK_SIG(env, methodID, _retsig, true);                            \
+        CHECK_STATIC_METHOD(env, clazz, methodID);                          \
         _retdecl;                                                           \
         va_list tmpArgs;                                                    \
         va_copy(tmpArgs, args);                                             \
@@ -1854,6 +1916,7 @@
         CHECK_ENTER(env, kFlag_Default);                                    \
         CHECK_CLASS(env, clazz);                                            \
         CHECK_SIG(env, methodID, _retsig, true);                            \
+        CHECK_STATIC_METHOD(env, clazz, methodID);                          \
         _retdecl;                                                           \
         CHECK_METHOD_ARGS_A(env, methodID, args);                           \
         _retasgn BASE_ENV(env)->CallStatic##_jname##MethodA(env, clazz,     \