This is a ping for any Reviewer and also a question for Vladimir.
Hello Vladimir,
What do you think about the classloader issue in the resolving of
classes in MemberName.getMethodType() described below?
Regards, Peter
On 04/23/2014 04:21 PM, Paul Sandoz wrote:
Hi Peter,
IMHO such security manager usage by the test is v. fragile and we should try
and find a safer alternative if possible.
However, there may also be an issue with lambda form code. (About a month ago i
too was looking, internally, at this kind of issue and thought there was a
questionable use of the application/system class loader when initializing the
LambdaForm class, but then i got distracted with other stuff and forgot about
it.)
Your one-liner fix seems to do the trick, but I think we need Vladimir to
confirm this is OK.
Paul.
On Apr 17, 2014, at 11:49 PM, Peter Levart <peter.lev...@gmail.com> wrote:
Hi,
I'm cross-posting this on the mlvm-dev mailing list, because I think it
concerns internal MHs implementation.
While replacing some inner classes with lambdas in java.lang.UNIXProcess class, a jtreg
test started failing. This test is employing a security manager with an unusual
configuration. It defines "java.util" as a package for which access should be
checked against the set of permissions. The class initialization of UNIXProcess class
initializes some lambdas and their initialization fails with the following stack-trace:
java.lang.ExceptionInInitializerError
at
java.lang.invoke.DirectMethodHandle.makePreparedLambdaForm(DirectMethodHandle.java:256)
at
java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:221)
at
java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:210)
at java.lang.invoke.DirectMethodHandle.make(DirectMethodHandle.java:82)
at
java.lang.invoke.MethodHandles$Lookup.getDirectMethodCommon(MethodHandles.java:1638)
at
java.lang.invoke.MethodHandles$Lookup.getDirectMethodNoSecurityManager(MethodHandles.java:1602)
at
java.lang.invoke.MethodHandles$Lookup.getDirectMethodForConstant(MethodHandles.java:1778)
at
java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(MethodHandles.java:1727)
at
java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:442)
at java.lang.UNIXProcess$Platform.get(UNIXProcess.java:147)
at java.lang.UNIXProcess.<clinit>(UNIXProcess.java:160)
at java.lang.ProcessImpl.start(ProcessImpl.java:130)
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1023)
at java.lang.Runtime.exec(Runtime.java:620)
at java.lang.Runtime.exec(Runtime.java:485)
at SecurityManagerClinit.main(SecurityManagerClinit.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:484)
at
com.sun.javatest.regtest.MainWrapper$MainThread.run(MainWrapper.java:94)
at java.lang.Thread.run(Thread.java:744)
Caused by: java.security.AccessControlException: access denied
("java.lang.RuntimePermission" "accessClassInPackage.java.util")
at
java.security.AccessControlContext.checkPermission(AccessControlContext.java:457)
at
java.security.AccessController.checkPermission(AccessController.java:884)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:541)
at
java.lang.SecurityManager.checkPackageAccess(SecurityManager.java:1481)
* at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:305)*
at java.lang.ClassLoader.loadClass(ClassLoader.java:359)
at
sun.invoke.util.BytecodeDescriptor.parseSig(BytecodeDescriptor.java:83)
* at
sun.invoke.util.BytecodeDescriptor.parseMethod(BytecodeDescriptor.java:54)*
at
sun.invoke.util.BytecodeDescriptor.parseMethod(BytecodeDescriptor.java:41)
at
java.lang.invoke.MethodType.fromMethodDescriptorString(MethodType.java:911)
at java.lang.invoke.MemberName.getMethodType(MemberName.java:144)
at
java.lang.invoke.LambdaForm.computeInitialPreparedForms(LambdaForm.java:477)
at java.lang.invoke.LambdaForm.<clinit>(LambdaForm.java:1641)
I think I found the root cause of the problem. It's nothing wrong with the test (although making
"java.util" as an access-checked package is a little unusual). The problem, I think, is
in MH's LambdaForm implementation. Although the UNIXProcess class is a system class (loaded by
bootstrap class loader), MHs created by evaluating lambda expressions in this class, trigger
loading a class in "java.util" package using AppClassLoader as the initiating class
loader, which involves package access checks. This should not happen, I think, if only the system
classes are involved in constructing MHs.
I checked the code and there's a ClassLoader parameter passed to the
*BytecodeDescriptor.parseMethod *method. This ClassLoader is taken as the
defining class loader of the MemberName's declaring class for which the
getMethoType() is requested. All the types involved in such MemberName (return
type, parameter types) should be resolvable from the MemberName's declaring
class' class loader. In our case, the class loader of the declaring class of
the particular MemberName is bootstrap class loader, so null is passed as the
ClassLoader parameter to the *BytecodeDescriptor.parseMethod *method. This
method checks for null ClassLoader and replaces it with
ClassLoader.getSystemClassLoader() before calling parseSig() with it, because
parseSig() uses the ClassLoader instance to invoke loadClass() method on it.
The ClassLoader.getSystemClassLoader() is the application class-loader
(AppClassLoader).
I think the right thing to do would be to use bootstrap class loader if the
ClassLoader parameter passed to *BytecodeDescriptor.parseMethod *was null. The
fix would be as follows:
===================================================================
--- jdk/src/share/classes/sun/invoke/util/BytecodeDescriptor.java (revision
9770:8371276d52c0)
+++ jdk/src/share/classes/sun/invoke/util/BytecodeDescriptor.java (revision
9770+:8371276d52c0+)
@@ -43,8 +43,6 @@
static List<Class<?>> parseMethod(String bytecodeSignature,
int start, int end, ClassLoader loader) {
- if (loader == null)
- loader = ClassLoader.getSystemClassLoader();
String str = bytecodeSignature;
int[] i = {start};
ArrayList<Class<?>> ptypes = new ArrayList<Class<?>>();
@@ -80,7 +78,7 @@
i[0] = endc+1;
String name = str.substring(begc, endc).replace('/', '.');
try {
- return loader.loadClass(name);
+ return Class.forName(name, false, loader);
} catch (ClassNotFoundException ex) {
throw new TypeNotPresentException(name, ex);
}
What do you think? Am I right?
Regards, Peter