8160987: JDWP ClassType.InvokeMethod doesn't validate class
authorhseigel
Wed, 21 Sep 2016 08:10:48 -0400
changeset 41359 23b0a20c3e3c
parent 41358 f2c5e3cc1848
child 41360 241663e51880
8160987: JDWP ClassType.InvokeMethod doesn't validate class Summary: Add code to validate class in JDWP instead of relying on JNI to do the check. Reviewed-by: dholmes, dcubed, sspitsyn, dsamersoff
jdk/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c
jdk/test/com/sun/jdi/InterfaceMethodsTest.java
--- a/jdk/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c	Tue Sep 20 10:37:32 2016 -0700
+++ b/jdk/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c	Wed Sep 21 08:10:48 2016 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -343,6 +343,35 @@
     debugMonitorExit(invokerLock);
 }
 
+/*
+ * Check that method is in the specified clazz or one of its super classes.
+ * We have to enforce this check at the JDWP layer because the JNI layer
+ * has different requirements.
+ */
+static jvmtiError check_methodClass(JNIEnv *env, jclass clazz, jmethodID method)
+{
+    jclass containing_class = NULL;
+    jvmtiError error;
+
+    error = JVMTI_FUNC_PTR(gdata->jvmti,GetMethodDeclaringClass)
+                (gdata->jvmti, method, &containing_class);
+    if (error != JVMTI_ERROR_NONE) {
+        return JVMTI_ERROR_NONE;  /* Bad jmethodID ?  This will be handled elsewhere */
+    }
+
+    if (JNI_FUNC_PTR(env,IsSameObject)(env, clazz, containing_class)) {
+        return JVMTI_ERROR_NONE;
+    }
+
+    // If not the same class then check that containing_class is a superclass of
+    // clazz (not a superinterface).
+    if (JNI_FUNC_PTR(env,IsAssignableFrom)(env, clazz, containing_class) &&
+        referenceTypeTag(containing_class) != JDWP_TYPE_TAG(INTERFACE)) {
+        return JVMTI_ERROR_NONE;
+    }
+    return JVMTI_ERROR_INVALID_METHODID;
+}
+
 jvmtiError
 invoker_requestInvoke(jbyte invokeType, jbyte options, jint id,
                       jthread thread, jclass clazz, jmethodID method,
@@ -353,6 +382,13 @@
     InvokeRequest *request;
     jvmtiError error = JVMTI_ERROR_NONE;
 
+    if (invokeType == INVOKE_STATIC) {
+        error = check_methodClass(env, clazz, method);
+        if (error != JVMTI_ERROR_NONE) {
+            return error;
+        }
+    }
+
     debugMonitorEnter(invokerLock);
     request = threadControl_getInvokeRequest(thread);
     if (request != NULL) {
--- a/jdk/test/com/sun/jdi/InterfaceMethodsTest.java	Tue Sep 20 10:37:32 2016 -0700
+++ b/jdk/test/com/sun/jdi/InterfaceMethodsTest.java	Wed Sep 21 08:10:48 2016 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -197,14 +197,18 @@
         // invoke interface static method A
         testInvokePos(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A));
 
-        // try to invoke static method A on the instance
-        testInvokePos(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A));
+        // invoking static method A on the instance fails because static method A is
+        // not inherited by TargetClass.
+        testInvokeNeg(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
+                "Invalid MethodID");
 
         // invoke interface static method B
         testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_A));
 
-        // try to invoke static method B on the instance
-        testInvokePos(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_A));
+        // invoking static method B on the instance fails because static method B is
+        // not inherited by TargetClass.
+        testInvokeNeg(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_A),
+                "Invalid MethodID");
 
         // try to invoke a virtual method
         testInvokePos(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(RESULT_A), true);
@@ -239,21 +243,25 @@
         testInvokeNeg(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
                 "Static interface methods are not inheritable");
 
-        // however it is possible to call "staticMethodA" on the actual instance
+        // "staticMethodA" is not inherited by InterfaceB even from an actual instance
         testInvokeNeg(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
                 "Static interface methods are not inheritable");
 
-        // "staticMethodB" is overridden in InterfaceB
+        // "staticMethodB" is re-defined in InterfaceB
         testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B));
 
-        // the instance invokes the overriden form of "staticMethodB" from InterfaceB
-        testInvokePos(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_B));
+        // the instance fails to invoke the re-defined form of "staticMethodB" from
+        // InterfaceB because staticMethodB is not inherited by TargetClass
+        testInvokeNeg(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
+                "Invalid MethodID");
 
         // "staticMethodC" is present only in InterfaceB
         testInvokePos(ifaceClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B));
 
-        // "staticMethodC" should be reachable from the instance too
-        testInvokePos(ifaceClass, ref, "staticMethodC", "()I", vm().mirrorOf(RESULT_B));
+        // "staticMethodC" is not reachable from the instance because staticMethodC
+        // is not inherited by TargetClass.
+        testInvokeNeg(ifaceClass, ref, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
+                "Invalid MethodID");
     }
 
     private void testImplementationClass(ReferenceType targetClass, ObjectReference thisObject) {