JDK-5092850 : RedefineClasses causes VerifyError
  • Type: Bug
  • Component: hotspot
  • Sub-Component: jvmti
  • Affected Version: 6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: solaris_2.5.1,solaris_9
  • CPU: x86,sparc
  • Submitted: 2004-08-26
  • Updated: 2020-04-09
  • Resolved: 2004-10-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6
5.0u3Fixed 6 mustangFixed
Related Reports
Duplicate :  
Relates :  
Description
Name: tb29552			Date: 08/26/2004


FULL PRODUCT VERSION :
java version "1.5.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta3-b60)
Java HotSpot(TM) Client VM (build 1.5.0-beta3-b60, mixed mode, sharing)


ADDITIONAL OS VERSION INFORMATION :
Linux deepspace1 2.4.22-10mdk #1 Thu Sep 18 12:30:58 CEST 2003 i686 unknown unknown GNU/Linux

A DESCRIPTION OF THE PROBLEM :
I have added a very simple example, that reproduces the problem:

The class (test.Startup) contains the instrumentation. It does nothing but replace the loaded class "test.Instrumented" with a new version (which is exactly the same as the old version). The loaded byte arrays is dumped to a file, so it is possible to compare it to the original classfile.

After reloading the class, invoking the constructor of "test.Instrumented" leads to a VerifyError. But since the classfile is exactly the same as before this should not happen.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1.) Make a directory "test"
2.) Create the three java files in this directory ("Startup.java", "Instrumentation.java", "Test.java")
3.) Save the manifest as "manifest.mf"
3.) Compile the classes
javac test/*.java
4.) Build the jar file
jar -cvfm test.jar manifest.mf test/Startup.class
5.) Run the Test
java -classpath . -javaagent:test.jar test.Test
6.) A VerifyError is thrown.
7.) Observe that result.dump has exactly the same contents as test/Instrumentation.class

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No Exception.
ACTUAL -
An VerifyError is thrown.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.VerifyError: (class: test/Instrument, method: <init> signature: ()V) Illegal constant pool index
        at test.Test.main(Test.java:12)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
----------------------------------------------
Instrumented.java:
----------------------------------------------
package test;

public class Instrument {
}
----------------------------------------------
Startup.java:
----------------------------------------------
package test;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.ClassDefinition;

public class Startup {
    private static Instrumentation instrumentation;

    public static void premain(String options, Instrumentation instrumentation) {
        Startup.instrumentation = instrumentation;
    }

    public static void init()
        throws Exception {
 
        Class[] allClasses = instrumentation.getAllLoadedClasses();
  
        for (int i=0; i<allClasses.length; i++) {
            String className = allClasses[i].getName();
            if ("test.Instrument".equals(className)) {
                byte[] classfileBuffer = getClassFileBuffer(className, allClasses[i].getClassLoader());
                instrumentation.redefineClasses(new ClassDefinition[] {new ClassDefinition(allClasses[i], classfileBuffer)});

                FileOutputStream fout = new FileOutputStream("result.dump");
                fout.write(classfileBuffer);
                fout.close();
            }
         }
    }

    private static final byte[] getClassFileBuffer(String className, ClassLoader cl) throws IOException {
        InputStream is = cl.getResourceAsStream(className.replace('.', '/') + ".class");
        byte[] buf = new byte[1024];

        ByteArrayOutputStream baos = new ByteArrayOutputStream(is.available());
        int read;
        do {
            read = is.read(buf);
            if (read > -1) {
                baos.write(buf, 0, read);
            }
        } while (read > -1);

        is.close();
        return baos.toByteArray();
    }
}
 
----------------------------------------------
Test.java:
----------------------------------------------
package test;

public class Test {

    public static void main(String[] args) throws Exception {
        // load the class
        System.out.println(Instrument.class.getName());

        Startup.init();

        // try to use it again -> causes VerifyError
        new Instrument();
    }
}
----------------------------------------------
manifest.mf:
----------------------------------------------
premain-class: test.Startup
Can-Redefine-Classes: true


---------- END SOURCE ----------
(Incident Review ID: 300972) 
======================================================================
###@###.### 10/7/04 00:12 GMT

Comments
SUGGESTED FIX http://oldsunweb.ireland/~ab23780/5092850/webrev/index.html ###@###.### 2004-11-23 20:19:15 GMT
23-11-2004

CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: mustang FIXED IN: mustang
29-09-2004

WORK AROUND The work-around is for the agent to assure the class is linked. For example by adding: classToRedefine.getDeclaredFields(); before the redefine.
29-09-2004

PUBLIC COMMENTS If the class isn't linked, RedefineClasses will throw a VerifyError.
29-09-2004

EVALUATION The test case redefines a class before it is linked. The redefine allocates a constant pool cache and rewrites the methods. However, later when the class is linked the verifier is invoked a second time on this class - on the second verification it is called to verify methods that have been re-written and hence the VerifyError. Possible solutions to examine are linking the class at redefine time (we already force all superclasses to be linked), or skip the (second) verification if the class has rewritten by redefine. ###@###.### 2004-08-27 Redefine now forces the class to be linked (previously only super classes and interfaces were linked). ###@###.### 2004-09-28
27-08-2004