Skip to content

Commit

Permalink
[JDK11] Fix AccessControlException in resolveInvokeDynamic
Browse files Browse the repository at this point in the history
MethodHandleResolver.resolveInvokeDynamic (linkage) relies upon
MethodType.fromMethodDescriptorString to derive the MethodType from
the method descriptor string.

Enabling OJDK's MethodType.fromMethodDescriptorString in OpenJ9 JDK11
causes an AccessControlException, which makes it unsuitable for usage
during linkage.

resolveInvokeDynamic (linkage) can employ its own approach to derive
the MethodType from the method descriptor string.

To resolve the AccessControlException, a helper method is derived from
OJ9's MethodType.fromMethodDescriptorString, and it is utilized in
MethodHandleResolver.

The helper also enables a Map based cache per ClassLoader in both
implementations. Currently, the cache is only available in OJ9 MHs.

Related: eclipse-openj9#14555

Signed-off-by: Babneet Singh <[email protected]>
  • Loading branch information
babsingh committed Oct 12, 2023
1 parent 8a817fe commit 570b507
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 83 deletions.
84 changes: 2 additions & 82 deletions jcl/src/java.base/share/classes/java/lang/invoke/MethodType.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
import java.util.WeakHashMap;

import com.ibm.oti.util.Msg;
import com.ibm.oti.vm.VM;

/*[IF CRIU_SUPPORT]*/
import openj9.internal.criu.NotCheckpointSafe;
Expand Down Expand Up @@ -337,43 +336,7 @@ public MethodType erase() {
*/
@VMCONSTANTPOOL_METHOD
public static MethodType fromMethodDescriptorString(String methodDescriptor, ClassLoader loader) {
ClassLoader classLoader = loader;
if (classLoader == null) {
/*[IF JAVA_SPEC_VERSION >= 14]*/
@SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
/*[ENDIF] JAVA_SPEC_VERSION >= 14 */
classLoader = ClassLoader.getSystemClassLoader();
}

// Check cache
Map<String, MethodType> classLoaderMethodTypeCache = VM.getVMLangAccess().getMethodTypeCache(classLoader);
MethodType mt = classLoaderMethodTypeCache != null ? classLoaderMethodTypeCache.get(methodDescriptor) : null;

// MethodDescriptorString is not in cache
if (null == mt) {
// ensure '.' is not included in the descriptor
if (methodDescriptor.indexOf((int)'.') != -1) {
throw new IllegalArgumentException(methodDescriptor);
}

// split descriptor into classes - last one is the return type
ArrayList<Class<?>> classes = parseIntoClasses(methodDescriptor, classLoader);
if (classes.size() == 0) {
throw new IllegalArgumentException(methodDescriptor);
}

Class<?> returnType = classes.remove(classes.size() - 1);
mt = methodType(returnType, classes);
if (classLoaderMethodTypeCache != null) {
classLoaderMethodTypeCache.put(mt.methodDescriptor, mt);
}
}

return mt;
return MethodTypeHelper.fromMethodDescriptorStringInternal(methodDescriptor, loader);
}

/**
Expand All @@ -385,7 +348,7 @@ public static MethodType fromMethodDescriptorString(String methodDescriptor, Cla
*/
@SuppressWarnings("unused") /* Used by native code */
private static final MethodType fromMethodDescriptorStringAppendArg(String methodDescriptor, ClassLoader loader, Class<?> appendArgumentType) {
List<Class<?>> types = parseIntoClasses(methodDescriptor, loader);
List<Class<?>> types = MethodTypeHelper.parseIntoClasses(methodDescriptor, loader);
Class<?> returnType = types.remove(types.size() - 1);
types.add(appendArgumentType);

Expand All @@ -412,49 +375,6 @@ private static final Throwable throwNoClassDefFoundError(TypeNotPresentException
}
throw e;
}

/*
* Parse the MethodDescriptor string into a list of Class objects. The last class in the list
* is the return type.
*/
private static final ArrayList<Class<?>> parseIntoClasses(String methodDescriptor, ClassLoader classLoader) {
int length = methodDescriptor.length();
if (length == 0) {
/*[MSG "K05d3", "invalid descriptor: {0}"]*/
throw new IllegalArgumentException(Msg.getString("K05d3", methodDescriptor)); //$NON-NLS-1$
}

char[] signature = new char[length];
methodDescriptor.getChars(0, length, signature, 0);
int index = 0;
boolean closeBracket = false;

if (signature[index] != '(') {
/*[MSG "K05d4", "missing opening '(': {0}"]*/
throw new IllegalArgumentException(Msg.getString("K05d4", methodDescriptor)); //$NON-NLS-1$
}
index++;

ArrayList<Class<?>> args = new ArrayList<Class<?>>();

while(index < length) {
/* Ensure we only see one ')' closing bracket */
if ((signature[index] == ')')) {
if (closeBracket) {
/*[MSG "K05d5", "too many ')': {0}"]*/
throw new IllegalArgumentException(Msg.getString("K05d5", methodDescriptor)); //$NON-NLS-1$
}
closeBracket = true;
index++;
continue;
}

index = MethodTypeHelper.parseIntoClass(signature, index, args, classLoader, methodDescriptor);
index++;
}
return args;
}


/**
* Convenience method to convert all types to Object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@
import jdk.internal.value.PrimitiveClass;
/*[ENDIF] INLINE-TYPES */

import java.util.Map;

import com.ibm.oti.util.Msg;
import com.ibm.oti.vm.VM;

/**
* MethodTypeHelper - static methods
Expand Down Expand Up @@ -270,6 +273,116 @@ static final int parseIntoClass(char[] signature, int index, ArrayList<Class<?>>
return index;
}

/**
* Parse the MethodDescriptor string into a list of Class objects. The last class in the list
* is the return type.
*
* @param methodDescriptor the method descriptor string
* @param classLoader the ClassLoader to be used or null for System ClassLoader
* @return list of classes representing the parameters and return type
* @throws IllegalArgumentException if the string is not well-formed
*/
static final ArrayList<Class<?>> parseIntoClasses(String methodDescriptor, ClassLoader classLoader) {
int length = methodDescriptor.length();
if (length == 0) {
/*[MSG "K05d3", "invalid descriptor: {0}"]*/
throw new IllegalArgumentException(Msg.getString("K05d3", methodDescriptor)); //$NON-NLS-1$
}

char[] signature = new char[length];
methodDescriptor.getChars(0, length, signature, 0);
int index = 0;
boolean closeBracket = false;

if (signature[index] != '(') {
/*[MSG "K05d4", "missing opening '(': {0}"]*/
throw new IllegalArgumentException(Msg.getString("K05d4", methodDescriptor)); //$NON-NLS-1$
}
index++;

ArrayList<Class<?>> args = new ArrayList<Class<?>>();

while(index < length) {
/* Ensure we only see one ')' closing bracket */
if ((signature[index] == ')')) {
if (closeBracket) {
/*[MSG "K05d5", "too many ')': {0}"]*/
throw new IllegalArgumentException(Msg.getString("K05d5", methodDescriptor)); //$NON-NLS-1$
}
closeBracket = true;
index++;
continue;
}

index = parseIntoClass(signature, index, args, classLoader, methodDescriptor);
index++;
}
return args;
}

/**
* Convenience Method to create a MethodType from bytecode-level method descriptor.
* (See JVM Spec 2nd Ed. section 4.4.3).
*
* All of the classes used in the method descriptor string must be reachable from a
* common ClassLoader or an exception will result.
*
* The ClassLoader parameter may be null, in which case the System ClassLoader will be used.
*
* Note, the Class names must use JVM syntax in the method descriptor String and therefore
* java.lang.Class will be represented as Ljava/lang/Class;
*
* Example method descriptors:
* (II)V - method taking two ints and return void
* (I)Ljava/lang/Integer; - method taking an int and returning an Integer
* ([I)I - method taking an array of ints and returning an int
*
* @param methodDescriptor the method descriptor string
* @param loader the ClassLoader to be used or null for System ClassLoader
* @return a MethodType object representing the method descriptor string
* @throws IllegalArgumentException if the string is not well-formed
* @throws TypeNotPresentException if a named type cannot be found
*/
static MethodType fromMethodDescriptorStringInternal(String methodDescriptor, ClassLoader loader) {
ClassLoader classLoader = loader;
if (classLoader == null) {
/*[IF JAVA_SPEC_VERSION >= 14]*/
@SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
/*[ENDIF] JAVA_SPEC_VERSION >= 14 */
classLoader = ClassLoader.getSystemClassLoader();
}

// Check cache
Map<String, MethodType> classLoaderMethodTypeCache = VM.getVMLangAccess().getMethodTypeCache(classLoader);
MethodType mt = classLoaderMethodTypeCache != null ? classLoaderMethodTypeCache.get(methodDescriptor) : null;

// MethodDescriptorString is not in cache
if (null == mt) {
// ensure '.' is not included in the descriptor
if (methodDescriptor.indexOf((int)'.') != -1) {
throw new IllegalArgumentException(methodDescriptor);
}

// split descriptor into classes - last one is the return type
ArrayList<Class<?>> classes = parseIntoClasses(methodDescriptor, classLoader);
if (classes.size() == 0) {
throw new IllegalArgumentException(methodDescriptor);
}

Class<?> returnType = classes.remove(classes.size() - 1);
mt = MethodType.methodType(returnType, classes);
if (classLoaderMethodTypeCache != null) {
classLoaderMethodTypeCache.put(mt.toMethodDescriptorString(), mt);
}
}

return mt;
}

/**
* This helper calls MethodType.fromMethodDescriptorString(...) or
* MethodType.fromMethodDescriptorStringAppendArg(...) but throws
Expand All @@ -287,7 +400,7 @@ static final int parseIntoClass(char[] signature, int index, ArrayList<Class<?>>
*/
static final MethodType vmResolveFromMethodDescriptorString(String methodDescriptor, ClassLoader loader, Class<?> appendArgumentType) throws Throwable {
try {
MethodType result = MethodType.fromMethodDescriptorString(methodDescriptor, loader);
MethodType result = fromMethodDescriptorStringInternal(methodDescriptor, loader);
if (null != appendArgumentType) {
result = result.appendParameterTypes(appendArgumentType);
}
Expand Down

0 comments on commit 570b507

Please sign in to comment.