4

I'm upgrading jdk 8 to 11.

I load some class in checkPermission method then security manager emit recursive update exception. but use jdk1.8.0_202 everything works fine.

What causes this problem?

  1. My environment.
OS: macOS 10.15.6
JDK(Oracle): 11.0.8
IDE: Intellij 2019 3
  1. Main
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello world");
    }
}
  1. SecurityManager
package sm;

import java.security.Permission;

public class MySecurityManager extends SecurityManager {

    @Override
    public void checkPermission(Permission permission) {

        // Problem occurs when load ServicePermission.class
        if (permission instanceof javax.security.auth.kerberos.ServicePermission) {
            // throw new SecurityException("javax.security.auth.kerberos.ServicePermission is not allowed.");
        }
    }

    @Override
    public void checkPermission(Permission permission, Object context) {
        this.checkPermission(permission);
    }
}


  1. Run with -Djava.security.manager=sm.MySecurityManager

  2. Console logs

Error occurred during initialization of VM
java.lang.BootstrapMethodError: bootstrap method initialization exception
    at java.lang.invoke.BootstrapMethodInvoker.invoke(java.base@11.0.8/BootstrapMethodInvoker.java:194)
    at java.lang.invoke.CallSite.makeSite(java.base@11.0.8/CallSite.java:307)
    at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(java.base@11.0.8/MethodHandleNatives.java:258)
    at java.lang.invoke.MethodHandleNatives.linkCallSite(java.base@11.0.8/MethodHandleNatives.java:248)
    at sun.net.www.protocol.jrt.JavaRuntimeURLConnection.<clinit>(java.base@11.0.8/JavaRuntimeURLConnection.java:55)
    at sun.net.www.protocol.jrt.Handler.openConnection(java.base@11.0.8/Handler.java:42)
    at java.net.URL.openConnection(java.base@11.0.8/URL.java:1074)
    at jdk.internal.module.SystemModuleFinders$SystemModuleReader.checkPermissionToConnect(java.base@11.0.8/SystemModuleFinders.java:405)
    at jdk.internal.module.SystemModuleFinders$SystemModuleReader.<init>(java.base@11.0.8/SystemModuleFinders.java:414)
    at jdk.internal.module.SystemModuleFinders$2.get(java.base@11.0.8/SystemModuleFinders.java:315)
    at jdk.internal.module.SystemModuleFinders$2.get(java.base@11.0.8/SystemModuleFinders.java:312)
    at jdk.internal.module.ModuleReferenceImpl.open(java.base@11.0.8/ModuleReferenceImpl.java:93)
    at jdk.internal.loader.BuiltinClassLoader$5.apply(java.base@11.0.8/BuiltinClassLoader.java:961)
    at jdk.internal.loader.BuiltinClassLoader$5.apply(java.base@11.0.8/BuiltinClassLoader.java:958)
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(java.base@11.0.8/ConcurrentHashMap.java:1705)
    at jdk.internal.loader.BuiltinClassLoader.moduleReaderFor(java.base@11.0.8/BuiltinClassLoader.java:969)
    at jdk.internal.loader.BuiltinClassLoader.defineClass(java.base@11.0.8/BuiltinClassLoader.java:731)
    at jdk.internal.loader.BuiltinClassLoader.lambda$findClassInModuleOrNull$2(java.base@11.0.8/BuiltinClassLoader.java:682)
    at java.security.AccessController.doPrivileged(java.base@11.0.8/Native Method)
    at jdk.internal.loader.BuiltinClassLoader.findClassInModuleOrNull(java.base@11.0.8/BuiltinClassLoader.java:683)
    at jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(java.base@11.0.8/BuiltinClassLoader.java:605)
    at jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(java.base@11.0.8/BuiltinClassLoader.java:640)
    at jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(java.base@11.0.8/BuiltinClassLoader.java:609)
    at jdk.internal.loader.BuiltinClassLoader.loadClass(java.base@11.0.8/BuiltinClassLoader.java:579)
    at jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(java.base@11.0.8/ClassLoaders.java:178)
    at java.lang.ClassLoader.loadClass(java.base@11.0.8/ClassLoader.java:521)
    at sm.MySecurityManager.checkPermission(MySecurityManager.java:11)
    at java.lang.SecurityManager.checkPropertyAccess(java.base@11.0.8/SecurityManager.java:1066)
    at java.lang.System.getProperty(java.base@11.0.8/System.java:814)
    at java.lang.ClassLoader.initSystemClassLoader(java.base@11.0.8/ClassLoader.java:1971)
    at java.lang.System.initPhase3(java.base@11.0.8/System.java:2070)
Caused by: java.lang.IllegalStateException: Recursive update
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(java.base@11.0.8/ConcurrentHashMap.java:1760)
    at jdk.internal.loader.BuiltinClassLoader.moduleReaderFor(java.base@11.0.8/BuiltinClassLoader.java:969)
    at jdk.internal.loader.BuiltinClassLoader.defineClass(java.base@11.0.8/BuiltinClassLoader.java:731)
    at jdk.internal.loader.BuiltinClassLoader.lambda$findClassInModuleOrNull$2(java.base@11.0.8/BuiltinClassLoader.java:682)
    at java.security.AccessController.doPrivileged(java.base@11.0.8/Native Method)
    at jdk.internal.loader.BuiltinClassLoader.findClassInModuleOrNull(java.base@11.0.8/BuiltinClassLoader.java:683)
    at jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(java.base@11.0.8/BuiltinClassLoader.java:605)
    at jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(java.base@11.0.8/BuiltinClassLoader.java:640)
    at jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(java.base@11.0.8/BuiltinClassLoader.java:609)
    at jdk.internal.loader.BuiltinClassLoader.loadClass(java.base@11.0.8/BuiltinClassLoader.java:579)
    at jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(java.base@11.0.8/ClassLoaders.java:178)
    at java.lang.ClassLoader.loadClass(java.base@11.0.8/ClassLoader.java:521)
    at sm.MySecurityManager.checkPermission(MySecurityManager.java:11)
    at java.lang.reflect.AccessibleObject.checkPermission(java.base@11.0.8/AccessibleObject.java:83)
    at java.lang.reflect.Constructor.setAccessible(java.base@11.0.8/Constructor.java:180)
    at java.lang.invoke.InnerClassLambdaMetafactory$1.run(java.base@11.0.8/InnerClassLambdaMetafactory.java:206)
    at java.lang.invoke.InnerClassLambdaMetafactory$1.run(java.base@11.0.8/InnerClassLambdaMetafactory.java:199)
    at java.security.AccessController.doPrivileged(java.base@11.0.8/Native Method)
    at java.lang.invoke.InnerClassLambdaMetafactory.buildCallSite(java.base@11.0.8/InnerClassLambdaMetafactory.java:198)
    at java.lang.invoke.LambdaMetafactory.metafactory(java.base@11.0.8/LambdaMetafactory.java:329)
    at java.lang.invoke.BootstrapMethodInvoker.invoke(java.base@11.0.8/BootstrapMethodInvoker.java:127)
    at java.lang.invoke.CallSite.makeSite(java.base@11.0.8/CallSite.java:307)
    at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(java.base@11.0.8/MethodHandleNatives.java:258)
    at java.lang.invoke.MethodHandleNatives.linkCallSite(java.base@11.0.8/MethodHandleNatives.java:248)
    at sun.net.www.protocol.jrt.JavaRuntimeURLConnection.<clinit>(java.base@11.0.8/JavaRuntimeURLConnection.java:55)
    at sun.net.www.protocol.jrt.Handler.openConnection(java.base@11.0.8/Handler.java:42)
    at java.net.URL.openConnection(java.base@11.0.8/URL.java:1074)
    at jdk.internal.module.SystemModuleFinders$SystemModuleReader.checkPermissionToConnect(java.base@11.0.8/SystemModuleFinders.java:405)
    at jdk.internal.module.SystemModuleFinders$SystemModuleReader.<init>(java.base@11.0.8/SystemModuleFinders.java:414)
    at jdk.internal.module.SystemModuleFinders$2.get(java.base@11.0.8/SystemModuleFinders.java:315)
    at jdk.internal.module.SystemModuleFinders$2.get(java.base@11.0.8/SystemModuleFinders.java:312)
    at jdk.internal.module.ModuleReferenceImpl.open(java.base@11.0.8/ModuleReferenceImpl.java:93)
    at jdk.internal.loader.BuiltinClassLoader$5.apply(java.base@11.0.8/BuiltinClassLoader.java:961)
    at jdk.internal.loader.BuiltinClassLoader$5.apply(java.base@11.0.8/BuiltinClassLoader.java:958)
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(java.base@11.0.8/ConcurrentHashMap.java:1705)
    at jdk.internal.loader.BuiltinClassLoader.moduleReaderFor(java.base@11.0.8/BuiltinClassLoader.java:969)
    at jdk.internal.loader.BuiltinClassLoader.defineClass(java.base@11.0.8/BuiltinClassLoader.java:731)
    at jdk.internal.loader.BuiltinClassLoader.lambda$findClassInModuleOrNull$2(java.base@11.0.8/BuiltinClassLoader.java:682)
    at java.security.AccessController.doPrivileged(java.base@11.0.8/Native Method)
    at jdk.internal.loader.BuiltinClassLoader.findClassInModuleOrNull(java.base@11.0.8/BuiltinClassLoader.java:683)
    at jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(java.base@11.0.8/BuiltinClassLoader.java:605)
    at jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(java.base@11.0.8/BuiltinClassLoader.java:640)
    at jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(java.base@11.0.8/BuiltinClassLoader.java:609)
    at jdk.internal.loader.BuiltinClassLoader.loadClass(java.base@11.0.8/BuiltinClassLoader.java:579)
    at jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(java.base@11.0.8/ClassLoaders.java:178)
    at java.lang.ClassLoader.loadClass(java.base@11.0.8/ClassLoader.java:521)
    at sm.MySecurityManager.checkPermission(MySecurityManager.java:11)
    at java.lang.SecurityManager.checkPropertyAccess(java.base@11.0.8/SecurityManager.java:1066)
    at java.lang.System.getProperty(java.base@11.0.8/System.java:814)
    at java.lang.ClassLoader.initSystemClassLoader(java.base@11.0.8/ClassLoader.java:1971)
    at java.lang.System.initPhase3(java.base@11.0.8/System.java:2070)


Process finished with exit code 1

kospi
  • 215
  • 3
  • 9
  • 1
    The easy "fix" would be to add a constructor that loads the necessary classes - or just calls `checkPermission` itself: `public MySecurityManager() { checkPermission(new AllPermission()); }` – Johannes Kuhn Aug 12 '20 at 18:30

1 Answers1

5

The stack trace indicates that the issue is connected with the module loading rather than class loading, which explains why you don’t have the problem in JDK 8 that doesn’t have modules.

When you read the stack trace starting at the bottom, i.e.

    at java.lang.System.initPhase3(java.base@11.0.8/System.java:2070)

you will encounter the stack frames

    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(java.base@11.0.8/ConcurrentHashMap.java:1705)
    at jdk.internal.loader.BuiltinClassLoader.moduleReaderFor(java.base@11.0.8/BuiltinClassLoader.java:969)

indicating an attempt to load a module. This will eventually end up at a privileged action that needs a check, so you’ll find the line

    at sm.MySecurityManager.checkPermission(MySecurityManager.java:11)

which triggers the loading of javax.security.auth.kerberos.ServicePermission which is in the module java.security.jgss which apparently has not been loaded before.

So the loadClass call ends up again at

    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(java.base@11.0.8/ConcurrentHashMap.java:1760)
    at jdk.internal.loader.BuiltinClassLoader.moduleReaderFor(java.base@11.0.8/BuiltinClassLoader.java:969)

which triggers the “java.lang.IllegalStateException: Recursive update”, as calling computeIfAbsent is not allowed from another computeIfAbsent call on the same ConcurrentHashMap. Since ignoring this constraint can lead to corrupted maps, a check has been added in Java 9 to reject such attempts. See this Q&A.


Generally, triggering class loading from a security manager that might get checked again during the class loading can be problematic. I suggest resorting to the documented toString() output for comparison. After all, that’s what the policy file based security implementation does as well.

Since ServicePermission is final, a cheaper permission.getClass().getName().equals( "javax.security.auth.kerberos.ServicePermission") would do as well. Both approaches avoid loading the permission if it has not been used before. As indicated by the issue, this may even save the loading of an entire module.

Holger
  • 243,335
  • 30
  • 362
  • 661