8145964: NoClassDefFound error in transforming lambdas

Skip VM anonymous classes in retransformation and give an error for redefinition.

Reviewed-by: dholmes, dcubed, never
This commit is contained in:
Coleen Phillimore 2016-08-19 14:54:31 -04:00
parent 5868fdc4b7
commit 85381e59e5
4 changed files with 197 additions and 15 deletions

View File

@ -7152,15 +7152,19 @@ class C2 extends C1 implements I2 {
returns <code>JNI_FALSE</code>) the class can be neither
redefined nor retransformed.
<p/>
Primitive classes (for example, <code>java.lang.Integer.TYPE</code>)
and array classes are never modifiable.
Primitive classes (for example, <code>java.lang.Integer.TYPE</code>),
array classes, and some implementation defined classes are never modifiable.
<p/>
</description>
<origin>new</origin>
<capabilities>
<capability id="can_redefine_any_class">
If possessed then all classes (except primitive and array classes)
are modifiable.
If possessed then all classes (except primitive, array, and some implementation defined
classes) are modifiable (redefine or retransform).
</capability>
<capability id="can_retransform_any_class">
If possessed then all classes (except primitive, array, and some implementation defined
classes) are modifiable with <functionlink id="RetransformClasses"/>.
</capability>
<capability id="can_redefine_classes">
No effect on the result of the function.
@ -9900,7 +9904,7 @@ myInit() {
</capabilityfield>
<capabilityfield id="can_redefine_any_class">
<description>
Can modify (retransform or redefine) any non-primitive non-array class.
Can modify (retransform or redefine) any modifiable class.
See <functionlink id="IsModifiableClass"/>.
</description>
</capabilityfield>
@ -10024,7 +10028,8 @@ myInit() {
</capabilityfield>
<capabilityfield id="can_retransform_any_class" since="1.1">
<description>
<functionlink id="RetransformClasses"/> can be called on any class
<functionlink id="RetransformClasses"/> can be called on any modifiable class.
See <functionlink id="IsModifiableClass"/>.
(<fieldlink id="can_retransform_classes" struct="jvmtiCapabilities"/>
must also be set)
</description>
@ -12494,8 +12499,8 @@ myInit() {
Otherwise, this event may be sent before the VM is initialized (the start
<functionlink id="GetPhase">phase</functionlink>).
Some classes might not be compatible
with the function (eg. ROMized classes) and this event will not be
generated for these classes.
with the function (eg. ROMized classes or implementation defined classes) and this event will
not be generated for these classes.
<p/>
The agent must allocate the space for the modified
class file data buffer
@ -14498,6 +14503,10 @@ typedef void (JNICALL *jvmtiEventVMInit)
- Add new capability can_generate_early_class_hook_events
- Add new function GetNamedModule
</change>
<change date="16 August 2016" version="9.0.0">
Clarified can_redefine_any_classes, can_retransform_any_classes and IsModifiableClass API to
disallow some implementation defined classes.
</change>
</changehistory>
</specification>

View File

@ -283,7 +283,7 @@ JvmtiEnv::RetransformClasses(jint class_count, const jclass* classes) {
return JVMTI_ERROR_INVALID_CLASS;
}
if (java_lang_Class::is_primitive(k_mirror)) {
if (!VM_RedefineClasses::is_modifiable_class(k_mirror)) {
return JVMTI_ERROR_UNMODIFIABLE_CLASS;
}
@ -294,9 +294,6 @@ JvmtiEnv::RetransformClasses(jint class_count, const jclass* classes) {
if (status & (JVMTI_CLASS_STATUS_ERROR)) {
return JVMTI_ERROR_INVALID_CLASS;
}
if (status & (JVMTI_CLASS_STATUS_ARRAY)) {
return JVMTI_ERROR_UNMODIFIABLE_CLASS;
}
instanceKlassHandle ikh(current_thread, k_oop);
if (ikh->get_cached_class_file_bytes() == NULL) {

View File

@ -130,7 +130,7 @@ bool VM_RedefineClasses::doit_prologue() {
}
oop mirror = JNIHandles::resolve_non_null(_class_defs[i].klass);
// classes for primitives and arrays cannot be redefined
// classes for primitives and arrays and vm anonymous classes cannot be redefined
// check here so following code can assume these classes are InstanceKlass
if (!is_modifiable_class(mirror)) {
_res = JVMTI_ERROR_UNMODIFIABLE_CLASS;
@ -250,9 +250,14 @@ bool VM_RedefineClasses::is_modifiable_class(oop klass_mirror) {
if (java_lang_Class::is_primitive(klass_mirror)) {
return false;
}
Klass* the_class_oop = java_lang_Class::as_Klass(klass_mirror);
Klass* k = java_lang_Class::as_Klass(klass_mirror);
// classes for arrays cannot be redefined
if (the_class_oop == NULL || !the_class_oop->is_instance_klass()) {
if (k == NULL || !k->is_instance_klass()) {
return false;
}
// Cannot redefine or retransform an anonymous class.
if (InstanceKlass::cast(k)->is_anonymous()) {
return false;
}
return true;

View File

@ -0,0 +1,171 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @library /test/lib
* @summary Test that retransforming and redefining anonymous classes gets UnmodifiableClassException
* @modules java.base/jdk.internal.misc
* @modules java.instrument
* jdk.jartool/sun.tools.jar
* @run main ModifyAnonymous buildagent
* @run main/othervm -javaagent:redefineagent.jar ModifyAnonymous
*/
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.lang.NoSuchFieldException;
import java.lang.NoSuchMethodException;
import java.lang.RuntimeException;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import jdk.test.lib.*;
public class ModifyAnonymous {
public static class LambdaTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer)
throws IllegalClassFormatException {
return null;
}
}
static Instrumentation inst = null;
static volatile boolean done = false;
public static void premain(String args, Instrumentation instrumentation) {
inst = instrumentation;
System.out.println("javaagent in da house!");
instrumentation.addTransformer(new LambdaTransformer());
}
private static void buildAgent() {
try {
ClassFileInstaller.main("ModifyAnonymous");
} catch (Exception e) {
throw new RuntimeException("Could not write agent classfile", e);
}
try {
PrintWriter pw = new PrintWriter("MANIFEST.MF");
pw.println("Premain-Class: ModifyAnonymous");
pw.println("Agent-Class: ModifyAnonymous");
pw.println("Can-Retransform-Classes: true");
pw.println("Can-Redefine-Classes: true");
pw.close();
} catch (FileNotFoundException e) {
throw new RuntimeException("Could not write manifest file for the agent", e);
}
sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar");
if (!jarTool.run(new String[] { "-cmf", "MANIFEST.MF", "redefineagent.jar", "ModifyAnonymous.class" })) {
throw new RuntimeException("Could not write the agent jar file");
}
}
public static class InstanceMethodCallSiteApp {
public static void test() throws InterruptedException {
for (int i = 0; i < 2; i++) {
InstanceMethodCallSiteApp app = new InstanceMethodCallSiteApp();
Runnable r = app::doWork; // this creates an anonymous class
while (!done) {
r.run();
Thread.sleep(10);
}
}
}
public void doWork() {
System.out.print(".");
}
}
static void runTest() {
while (!done) {
Class[] allLoadedClasses = inst.getAllLoadedClasses();
for (Class clazz : allLoadedClasses) {
final String name = clazz.getName();
if (name.contains("$$Lambda$") && name.contains("App")) {
if (inst.isModifiableClass(clazz)) {
throw new RuntimeException ("Class should not be modifiable");
}
// Try to modify them anyway.
try {
System.out.println("retransform called for " + name);
inst.retransformClasses(clazz);
} catch(java.lang.instrument.UnmodifiableClassException t) {
System.out.println("PASSED: expecting UnmodifiableClassException");
t.printStackTrace();
}
try {
System.out.println("redefine called for " + name);
String newclass = "class Dummy {}";
byte[] bytecode = InMemoryJavaCompiler.compile("Dummy", newclass);
ClassDefinition cld = new ClassDefinition(clazz, bytecode);
inst.redefineClasses(new ClassDefinition[] { cld });
} catch(java.lang.instrument.UnmodifiableClassException t) {
System.out.println("PASSED: expecting UnmodifiableClassException");
t.printStackTrace();
} catch(java.lang.ClassNotFoundException e) {
throw new RuntimeException ("ClassNotFoundException thrown");
}
done = true;
}
}
}
}
public static void main(String argv[]) throws InterruptedException, RuntimeException {
if (argv.length == 1 && argv[0].equals("buildagent")) {
buildAgent();
return;
}
if (inst == null) {
throw new RuntimeException("Instrumentation object was null");
}
new Thread() {
public void run() {
runTest();
}
}.start();
// Test that NCDFE is not thrown for anonymous class:
// ModifyAnonymous$InstanceMethodCallSiteApp$$Lambda$18
try {
ModifyAnonymous test = new ModifyAnonymous();
InstanceMethodCallSiteApp.test();
} catch (NoClassDefFoundError e) {
throw new RuntimeException("FAILED: NoClassDefFoundError thrown for " + e.getMessage());
}
System.out.println("PASSED: NoClassDefFound error not thrown");
}
}