Skip to content

Commit

Permalink
8261154: Memory leak in Java_java_lang_ClassLoader_defineClass0 with …
Browse files Browse the repository at this point in the history
…long class names

Reviewed-by: stuefe, chegar, mchung
  • Loading branch information
cl4es committed Feb 5, 2021
1 parent 78b0d32 commit 0791899
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/java.base/share/native/libjava/ClassLoader.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ Java_java_lang_ClassLoader_defineClass0(JNIEnv *env,

result = JVM_LookupDefineClass(env, lookup, utfName, body, length, pd, initialize, flags, classData);

if (utfName && utfName != buf)
free(utfName);

free_body:
free(body);
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;

public class LookupDefineClass {
/**
* foo.Foo
*/
private static final byte[] X_BYTECODE = new byte[]{
(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE, 0x00, 0x00, 0x00, 0x38, 0x00, 0x10, 0x0A, 0x00,
0x03, 0x00, 0x0C, 0x07, 0x00, 0x0D, 0x07, 0x00, 0x0E, 0x07, 0x00, 0x0F,
Expand All @@ -62,6 +65,61 @@ public class LookupDefineClass {
0x02, 0x00, 0x0B
};

/**
* A variant of X_BYTECODE, with the class name rewritten to trigger malloc in ClassLoader.c
*
* ClassReader reader = new ClassReader(X_BYTECODE);
* ClassWriter writer = new ClassWriter(reader, 0);
* reader.accept(new ClassVisitor(Opcodes.ASM5, writer) {
* @Override
* public void visit(int version, int access, String name,
* String signature, String superName, String[] interfaces) {
* super.visit(version, access,
* "foo/AReallyReallyLongClassNameThatWillSurelyNotFitInACharBufferOfLength128ToTestThatWeDontLeakMemoryWhenTheClassNameIsTooLongForTheStackAllocatedBuffer",
* signature, superName, interfaces);
* }
* }, 0);
* byte[] code = writer.toByteArray();
* System.out.println(HexFormat.ofDelimiter(", ").withPrefix("0x").formatHex(code));
*/
private static final byte[] X_LONG_NAME_BYTECODE = new byte[]{
(byte)0xca, (byte)0xfe, (byte)0xba, (byte)0xbe, 0x00, 0x00, 0x00, 0x38, 0x00, 0x12, 0x0a, 0x00,
0x03, 0x00, 0x0c, 0x07, 0x00, 0x0d, 0x07, 0x00, 0x0e, 0x07, 0x00, 0x0f,
0x01, 0x00, 0x06, 0x3c, 0x69, 0x6e, 0x69, 0x74, 0x3e, 0x01, 0x00, 0x03,
0x28, 0x29, 0x56, 0x01, 0x00, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x01, 0x00,
0x0f, 0x4c, 0x69, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x54,
0x61, 0x62, 0x6c, 0x65, 0x01, 0x00, 0x03, 0x72, 0x75, 0x6e, 0x01, 0x00,
0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x01,
0x00, 0x08, 0x46, 0x6f, 0x6f, 0x2e, 0x6a, 0x61, 0x76, 0x61, 0x0c, 0x00,
0x05, 0x00, 0x06, 0x01, 0x00, 0x07, 0x66, 0x6f, 0x6f, 0x2f, 0x46, 0x6f,
0x6f, 0x01, 0x00, 0x10, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e,
0x67, 0x2f, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x01, 0x00, 0x12, 0x6a,
0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x52, 0x75, 0x6e,
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x01, 0x00, (byte)0x97, 0x66, 0x6f, 0x6f, 0x2f,
0x41, 0x52, 0x65, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x61, 0x6c, 0x6c,
0x79, 0x4c, 0x6f, 0x6e, 0x67, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61,
0x6d, 0x65, 0x54, 0x68, 0x61, 0x74, 0x57, 0x69, 0x6c, 0x6c, 0x53, 0x75,
0x72, 0x65, 0x6c, 0x79, 0x4e, 0x6f, 0x74, 0x46, 0x69, 0x74, 0x49, 0x6e,
0x41, 0x43, 0x68, 0x61, 0x72, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x4f,
0x66, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x31, 0x32, 0x38, 0x54, 0x6f,
0x54, 0x65, 0x73, 0x74, 0x54, 0x68, 0x61, 0x74, 0x57, 0x65, 0x44, 0x6f,
0x6e, 0x74, 0x4c, 0x65, 0x61, 0x6b, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79,
0x57, 0x68, 0x65, 0x6e, 0x54, 0x68, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73,
0x4e, 0x61, 0x6d, 0x65, 0x49, 0x73, 0x54, 0x6f, 0x6f, 0x4c, 0x6f, 0x6e,
0x67, 0x46, 0x6f, 0x72, 0x54, 0x68, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b,
0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x42, 0x75, 0x66,
0x66, 0x65, 0x72, 0x07, 0x00, 0x10, 0x00, 0x21, 0x00, 0x11, 0x00, 0x03,
0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05,
0x00, 0x06, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x01,
0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x2a, (byte)0xb7, 0x00, 0x01, (byte)0xb1, 0x00,
0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x06, 0x00, 0x01, 0x00,
0x07, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, (byte)0xb1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06,
0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x00,
0x00, 0x02, 0x00, 0x0b
};

/**
* Our own crippled classloader, that can only load a simple class over and over again.
*/
Expand Down Expand Up @@ -207,6 +265,15 @@ public Class<?> load() throws ClassNotFoundException {
}
}

@Benchmark
public Class<?> loadLongName() throws ClassNotFoundException {
try {
return HOST_LOOKUP.defineHiddenClass(X_LONG_NAME_BYTECODE, false).lookupClass();
} catch (IllegalAccessException e) {
throw new InternalError(e);
}
}

public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(LookupDefineClass.WeakClass.class.getSimpleName())
Expand Down Expand Up @@ -276,6 +343,15 @@ public Class<?> load() throws ClassNotFoundException {
}
}

@Benchmark
public Class<?> loadLongName() throws ClassNotFoundException {
try {
return HOST_LOOKUP.defineHiddenClass(X_LONG_NAME_BYTECODE, false, STRONG).lookupClass();
} catch (IllegalAccessException e) {
throw new InternalError(e);
}
}

public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(LookupDefineClass.HiddenClass.class.getSimpleName())
Expand Down

1 comment on commit 0791899

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.