Leaking instance of uninitialized classes

Frederic Parain frederic.parain at oracle.com
Wed Aug 14 20:13:27 UTC 2019


This is a follow up on the discussion about static
inline fields and the risk of having a default value
escaping while the inline class is uninitialized (the
problematic scenario being when the class fails to
initialize properly).

This situation already exists today, it is possible
to produce it with this code for instance (the discussion
continues after the code):

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class UninitilizedClassTest {
    public static A a;
    public static boolean b = true;

    static class A {
        public int i;
        public Object o;
        static long l;
        static {
            UninitilizedClassTest.a = new A();
            if (UninitilizedClassTest.b) {
                throw new Error();
            }
        }
        static void static_print() {
            System.out.println("Hello!");
        }
        void nonstatic_print() {
            System.out.println("i=" + i);
        }
    }

    public static void main(String[] args) {
        try {
            A test_a = new A();
        } catch(Throwable t) { }
        System.out.println("Accessing instance fields");
        System.out.println("a.o=" + a.o);
        System.out.println("Invoking non-static methods");
        a.nonstatic_print();
        System.out.println("Accessing static fields");
        try {
            System.out.println("A.l=" + A.l);
        } catch (Throwable t) {
            t.printStackTrace();
        }
        System.out.println("Invoking static methods");
        Method m = null;
        try {
            m = a.getClass().getDeclaredMethod("static_print", null);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        try {
            m.invoke(null);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

This situation is known, and the way the JVM deals with it is:
  - access to the leaked instance (fields, methods) is fine
  - access to the static context of the class which failed to
    initialized properly (fields, methods, new) throws an exception

Here’s the output of the program above:

Accessing instance fields
a.o=null
Invoking non-static methods
i=0
Accessing static fields
java.lang.NoClassDefFoundError: Could not initialize class UninitilizedClassTest$A
	at UninitilizedClassTest.main(UninitilizedClassTest.java:36)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)
Invoking static methods
Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class UninitilizedClassTest$A
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at UninitilizedClassTest.main(UninitilizedClassTest.java:48)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)


What is new with static inline fields?

It just becomes simpler to produce this scenario.
With regular classes, it requires a faulty static initializer
to leak an instance to a publicly accessible place before
the class initialization fails to complete.
With static inline classes, any static inline field could
potentially produce this situation.

Do we want to provide more protection for inline classes than
we actually provide for identity classes? The question is why?

Should we provide compile time warnings? They are likely
to generate a lot of false positive.

Last time I’ve investigated this issue and discussed it with
Alex Buckley, I came to the conclusion that it was not necessary
to do additional work for inline classes. 

But the EG can have a different opinion.

Fred
 





More information about the valhalla-spec-observers mailing list