JDK-8022718 : Runtime accessibility checking: protected class, if extended, should be accessible from another package
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: hs25,8
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2013-08-09
  • Updated: 2021-04-29
  • Resolved: 2013-10-18
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.
JDK 8 Other
8Fixed hs25Fixed
Related Reports
Relates :  
Relates :  
Description
JSR 335 spec, chapter "15.28.2 Run-time Evaluation of Method References" contains following assertion:

If the method reference has the form ExpressionName :: NonWildTypeArgumentsopt Identifier or Primary :: NonWildTypeArgumentsopt Identifier, the body of the invocation method has the effect of invoking the compile-time declaration of the method reference, as described in 15.12.4.3, 15.12.4.4, 15.12.4.5.[jsr335-15.28.2-41]

This assertions refers to chapter "15.12.4.3. Check Accessibility of Type and Method" which contains following assertions:

Let C be the class containing the method invocation, and let T be the qualifying type of the method invocation (��13.1), and let m be the name of the method as determined at compile-time (��15.12.3).
...
If T is in a different package than C, and T is protected, then T is accessible if and only if C is a subclass of T.
...

According to these assertions MethodInvoker.invoke should succeed, provided, the class files resulted form following sources compilation are supplied at run-time:

MethodInvoker.java:
import anotherpkg.MethodSupplierOuter;
import anotherpkg.MethodSupplierFactory;

public class MethodInvoker extends MethodSupplierOuter.MethodSupplier {
    public static void invoke() throws Exception {
        MethodInvoker ms = (MethodInvoker)MethodSupplierFactory.create();
        MyFunctionalInterface fi = ms::m;
        fi.invokeMethodReference();
    }
}

MethodSupplierOuter.java:
package anotherpkg;

public class MethodSupplierOuter {
    protected static class MethodSupplier {
        public void m() {
            System.out.println("bad");
        }    
    }
}

MethodSupplierFactory.java:
package anotherpkg;

public class MethodSupplierFactory {
    public static Object create() throws Exception {
        return Class.forName("MethodInvoker").newInstance();
    }
}


but exception is thrown instead resulting in following output:


Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:491)
	at Test.main(Test.java:7)
Caused by: java.lang.IncompatibleClassChangeError
	at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:383)
	at MethodInvoker.invoke(MethodInvoker.java:7)
	... 5 more
Caused by: java.lang.IllegalAccessException: symbolic reference class is not public: class anotherpkg.MethodSupplierOuter$MethodSupplier, from MethodInvoker
	at java.lang.invoke.MemberName.makeAccessException(MemberName.java:744)
	at java.lang.invoke.MethodHandles$Lookup.checkSymbolicClass(MethodHandles.java:1026)
	at java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:1017)
	at java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(MethodHandles.java:1284)
	at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:381)
	... 6 more

This was reproduced on b100, platform: Windows7x64.

The minimized test is attached.
The steps to reproduce the error are below, they should be performed in exactly specified order:

1) compile Test.java from archive;
2) compile sources from "anotherpkg" package from archive;
3) compile MethodInvoker.java from archive;
4) compile bad/MethodSupplier.java from archive;
5) run Test.main.

Comments
Please, fix "Resolved In Build" field. Resolved In Build: 53 , but Introduced In Build: 100.
25-10-2013

I would think that #1 should be rejected by the JVM verifier. But I guess not; JVMS 4.1 says "All bits of the access_flags item not assigned in Table 4.1 are reserved for future use. They should be set to zero in generated class files and should be ignored by Java Virtual Machine implementations." The internal access flags are normative for JSR 292 elements, since they emulate bytecode behavior, not Java program element behavior. The inner classes flags are not normative and should be ignored. BTW, a Java protected class is compiled to a JVM public class.
09-10-2013

In the process of writing a standalone regression test for this, I noticed the following interesting behavior, which I think we want to be aware of, ESPECIALLY since this is most likely to be the result of bytecode hacking: There are two places where the accessibility might be modified -- either at the "inner class" attribute, or in the class file bits itself. I decided to try modifying either one or both of these to be PROTECTED to see what I got, and I got different exceptions: #1 modifying the access flags of the class itself: V: name = anotherpkg/MethodSupplierOuter$MethodSupplier java.lang.IllegalAccessError: class MethodInvoker cannot access its superclass anotherpkg.MethodSupplierOuter$MethodSupplier at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:752) at java.lang.ClassLoader.defineClass(ClassLoader.java:634) at BogoLoader.loadClass(BogoLoader.java:137) at java.lang.ClassLoader.loadClass(ClassLoader.java:356) at Test.tryModifiedInvocation(Test.java:75) at Test.main(Test.java:59) #2 Modifying the access flags of the inner class attribute: VIC: name = anotherpkg/MethodSupplierOuter$MethodSupplier, outerName = anotherpkg/MethodSupplierOuter, innerName = MethodSupplier java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:491) at Test.tryModifiedInvocation(Test.java:76) at Test.main(Test.java:60) Caused by: java.lang.IllegalAccessError: symbolic reference class is not public: class anotherpkg.MethodSupplierOuter$MethodSupplier, from MethodInvoker at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:444) at MethodInvoker.invoke(MethodInvoker.java:6) ... 6 more Caused by: java.lang.IllegalAccessException: symbolic reference class is not public: class anotherpkg.MethodSupplierOuter$MethodSupplier, from MethodInvoker at java.lang.invoke.MemberName.makeAccessException(MemberName.java:812) at java.lang.invoke.MethodHandles$Lookup.checkSymbolicClass(MethodHandles.java:1108) at java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:1099) at java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(MethodHandles.java:1363) at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:442) ... 7 more #3, modifying both, fails same as #1 because that fails first. V: name = anotherpkg/MethodSupplierOuter$MethodSupplier VIC: name = anotherpkg/MethodSupplierOuter$MethodSupplier, outerName = anotherpkg/MethodSupplierOuter, innerName = MethodSupplier java.lang.IllegalAccessError: class MethodInvoker cannot access its superclass anotherpkg.MethodSupplierOuter$MethodSupplier at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:752) at java.lang.ClassLoader.defineClass(ClassLoader.java:634) at BogoLoader.loadClass(BogoLoader.java:137) at java.lang.ClassLoader.loadClass(ClassLoader.java:356) at Test.tryModifiedInvocation(Test.java:75) at Test.main(Test.java:61) I assume we care about the behavior that matches the bug, but I figure it does not hurt to be aware of this. The language in the JVM Spec 7 is vague: "Oracle's Java Virtual Machine implementation does not check the consistency of an InnerClasses attribute against a class file representing a class or interface referenced by the attribute." (this assumes that the ASM visitInnerClass is visiting the attribute, which I think it is).
03-10-2013

Test name moved from JCK-7301601 to make Aurora happy. lang/LMBD/lmbd140/lmbd14007m01101/lmbd14007m01101_rt lang/LMBD/lmbd140/lmbd14007m1011/lmbd14007m1011_rt lang/LMBD/lmbd140/lmbd14007m401/lmbd14007m401_rt lang/LMBD/lmbd140/lmbd14008m01101/lmbd14008m01101_rt lang/LMBD/lmbd140/lmbd14008m1011/lmbd14008m1011_rt lang/LMBD/lmbd140/lmbd14008m21111/lmbd14008m21111_rt lang/LMBD/lmbd140/lmbd14008m3012/lmbd14008m3012_rt lang/LMBD/lmbd140/lmbd14008m4002/lmbd14008m4002_rt lang/LMBD/lmbd140/lmbd14008m41121/lmbd14008m41121_rt lang/LMBD/lmbd140/lmbd14008m601/lmbd14008m601_rt
27-09-2013

Following JCK8 Compiler Suite tests fail due to this issue: lang/LMBD/lmbd140/lmbd14008m01101/lmbd14008m01101.html lang/LMBD/lmbd140/lmbd14008m1011/lmbd14008m1011.html lang/LMBD/lmbd140/lmbd14008m21111/lmbd14008m21111.html lang/LMBD/lmbd140/lmbd14008m601/lmbd14008m601.html
03-09-2013

VerifyAccess.isClassAccessible is consulting Class.getModifiers, in which the PUBLIC access flag has been adjusted to PROTECTED. The bytecode behavior of the corresponding method handle constant would treat the class as public. Consider using Reflection.getClassAccessFlags instead of Class.getModifiers, to get the same view of access bits as used by Reflection::verify_class_access in the JVM.
09-08-2013