11 Replies Latest reply on Feb 16, 2010 3:36 PM by dongguang zhou

    Deadlock and infinite loop between BaseClassLoader and Java Policy Provider

    dongguang zhou Newbie

      A custom security provider is set as the default Java Policy, e.g. java.security.Policy.setPolicy(customProvider),  in JBoss 5.2.0 server startup,

      and we ran into deadlock and infinite loop between the custom policy provider and BaseClassLoader.

       

      The issue is caused by the cyclic dependency between the customer policy provider and JBoss classloader (BaseClassLoader).  After the custom policy provider is set as the default Java security policy in startup, JBoss classloader invokes getPermissions() API of java.security.Policy to create new ProtectionDomain when loading new classes, and the custom provider invokes JBoss classloader for any new class instances as well. it then runs infinite loop between them when they call each other simultenously.

       

      The deadlock occurs when each is holding its own lock and trying to call each other for new class instances and getPermission().

       

      With some debugging into BaseClassLoader and VFSClassLoaderPolicy, I found VFSClassLoaderPolicy calls getPermission() and create protectiondomain for BaseClassLoader, and I don't see how BaseClassLoader uses Permissions in ProtectionDomain other than the CodeSource, please advice.

       

      To solve my deadlock and infinite loop issue, I'd like to know if it's configurable to disable the dependency on java.security.Policy, or specify a different Java Policy for VFSClassLoaderPolicy.

        • 1. Re: Deadlock and infinite loop between BaseClassLoader and Java Policy Provider
          Ales Justin Master

          To solve my deadlock and infinite loop issue, I'd like to know if it's configurable to disable the dependency on java.security.Policy, or specify a different Java Policy for VFSClassLoaderPolicy.

          You can change the CLPolicy, by changing the CLDeployer.

          See VFSClassLoaderDescribeDeployer in conf/bootstrap/deployers.xml.

           

          Can you provide a thread snapshot of this lock and post it here?

          • 3. Re: Deadlock and infinite loop between BaseClassLoader and Java Policy Provider
            dongguang zhou Newbie

            In the following bean descriptor of conf/bootstrap/deployers.xml,

               <bean name="ClassLoaderDescribeDeployer" class="org.jboss.deployers.vfs.plugins.classloader.VFSClassLoaderDescribeDeployer">
                  <property name="classLoading"><inject bean="ClassLoading"/></property>
               </bean>

            Is it possible to specify a different ClassLoaderPolicy to replace the default VFSClassLoaderPolicy for the system classloader?

            • 4. Re: Deadlock and infinite loop between BaseClassLoader and Java Policy Provider
              Adrian Brock Master

              I don't think this is a JBoss issue.


              I can reproduce the problem with a slightly modified URLClassLoader - the modification is only to move the test.support.* classes

              (including the policy implementation) outside the bootstrap classloader.

               

              package test;
              
              import java.io.ByteArrayOutputStream;
              import java.io.InputStream;
              import java.net.URL;
              import java.net.URLClassLoader;
              import java.security.CodeSource;
              import java.security.PermissionCollection;
              import java.security.Policy;
              import java.security.ProtectionDomain;
              
              public class EmulateNonBootstrapClassLoader extends URLClassLoader
              {
                 public EmulateNonBootstrapClassLoader()
                 {
                    super(new URL[0]);
                 }
                 
                 protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
                 {
                    System.out.println ("loadClass: " + name);
                    
                    if (name.startsWith("test.support"))
                    {
                       byte[] buffer = loadByteCode(name);
                       ProtectionDomain pd = getProtectionDomain();
                       return defineClass(name, buffer, 0, buffer.length, pd);
                    }
                    return super.loadClass(name, resolve);
                 }
              
                 private byte[] loadByteCode(String name) throws ClassNotFoundException
                 {
                    try
                    {
                       String resourceName = name.replace('.', '/') + ".class";
                       InputStream stream = getResourceAsStream(resourceName);
                       ByteArrayOutputStream baos = new ByteArrayOutputStream();
                       byte[] buffer = new byte[1024];
                       int bytes = stream.read(buffer);
                       while (bytes >= 0)
                       {
                          baos.write(buffer, 0, bytes);
                          bytes = stream.read(buffer);
                       }
                       return baos.toByteArray();
                    }
                    catch (Exception e)
                    {
                       throw new ClassNotFoundException("CNFE: " + name, e);
                    }
                 }
              
                 private ProtectionDomain getProtectionDomain()
                 {
                    // The actual codesource doesn't matter it is in the classloading in getPermissions that causes the problem 
                    CodeSource cs = getClass().getProtectionDomain().getCodeSource();
                    PermissionCollection pc = Policy.getPolicy().getPermissions(cs); 
                    return new ProtectionDomain(cs, pc);
                 }
              }
              

               

              The key issue is that the policy is loading classes from the non-bootstrap classloader which leads to recursion/deadlock

              i.e. it can't define the policy for the new class until it can load the new class.

              So I created a simple policy that does a classloading request from the same classloader during getPermissions().

               

              package test.support;
              
              import java.security.CodeSource;
              import java.security.PermissionCollection;
              import java.security.Policy;
              
              public class TestPolicyProvider extends Policy
              {
                 public PermissionCollection getPermissions(CodeSource cs)
                 {
                    try
                    {
                       getClass().getClassLoader().loadClass("test.support.B");
                    }
                    catch (ClassNotFoundException e)
                    {
                       throw new Error("Error loading B", e);
                    }
                    return super.getPermissions(cs);
                 }
              }
              
              

               

              The test driver is the following

              package test;
              
              import java.security.Policy;
              
              public class Main
              {
                 public static void main(String[] args) throws Exception
                 {
                    // Create a non-bootstrap classloader
                    ClassLoader cl = new EmulateNonBootstrapClassLoader();
                    
                    // Load the policy provider from it
                    Class<?> clazz = cl.loadClass("test.support.TestPolicyProvider");
                    dumpClass(clazz);
              
                    // Create a new instance and install it
                    Policy policy = (Policy) clazz.newInstance();
                    Policy.setPolicy(policy);
                    
                    // Now try to load a class, it will loop when the policy tries to load test.support.B
                    clazz = cl.loadClass("test.support.A");
                    dumpClass(clazz);
                 }
                 
                 public static void dumpClass(Class<?> clazz)
                 {
                    System.out.println(clazz.getName() + " cl=" +clazz.getClassLoader() + " pd=" + clazz.getProtectionDomain());
                 }
              }
              
              

               

              The classes A and B are just simple empty classes. The important part is they are not in the bootstrap classloader.

               

              CONCLUSION:

               

              None of the code above contains any jboss specific code. The issue is caused by where the Policy's support classes are located.

               

              This is only works out of the box if the Policy implementation and its support classes are in the bootstrap classloader, i.e. the classpath.

              You can see this if you change the first line of Main to use ClassLoader.getSystemClassLoader().

              Presumably the bootstrap classloader is doing something special to avoid the problem?

              • 5. Re: Deadlock and infinite loop between BaseClassLoader and Java Policy Provider
                Adrian Brock Master

                A simple workaround would be to preload the Policy's support classes before installing it.

                In this case, it looks to be a custom principal class from the stack trace?


                • 6. Re: Deadlock and infinite loop between BaseClassLoader and Java Policy Provider
                  dongguang zhou Newbie

                  Thank you for reproducing this issue clearly. I agreed the simplest and best solution is to use bootstrap classloader for custom policy povider and its dependent classes. On the flip side, it also points out that JBoss classloader has issue with custom policy provider in Java policy classloading, if the policy provider is loaded by system classloader and the policies with new class instances are not loaded at server startup before it's set as the default policy provider, the infinite loop problem may occur.

                  • 7. Re: Deadlock and infinite loop between BaseClassLoader and Java Policy Provider
                    dongguang zhou Newbie
                    Any docs for configuring VFSClassLoaderDescribeDeployer in bootstrap/deployers.xml with regard to ClassLoaderPolicy?
                    • 9. Re: Deadlock and infinite loop between BaseClassLoader and Java Policy Provider
                      dongguang zhou Newbie

                      Thank you for the reply. I'm following your example and looking into the approach to create custom ClassLoaderPolicyModule, ClassLoaderDescribeDeployer and ClassLoaderPolicy and by-pass the getProtectionDomain() and fix this issue.

                       

                      In method determinePolicy() of DecrypterClassLoaderPolicyModule which inherits VFSDeploymentClassLoaderPolicyModule, it calls getRoots() and getExcludedRoots() when instantiating CrypterClassLoaderPolicy, it doesn't seem that both methods are part of VFSDeploymentClassLoaderPolicyModule or its parent classes.

                       

                      CrypterClassLoaderPolicy(getContextName(), getRoots(), getExcludedRoots(), decrypter);

                       

                      Am I miss anything here?

                      • 10. Re: Deadlock and infinite loop between BaseClassLoader and Java Policy Provider
                        Ales Justin Master

                        In method determinePolicy() of DecrypterClassLoaderPolicyModule which inherits VFSDeploymentClassLoaderPolicyModule, it calls getRoots() and getExcludedRoots() when instantiating CrypterClassLoaderPolicy, it doesn't seem that both methods are part of VFSDeploymentClassLoaderPolicyModule or its parent classes.

                         

                        CrypterClassLoaderPolicy(getContextName(), getRoots(), getExcludedRoots(), decrypter);

                         

                        Am I miss anything here?

                        You're probably looking at an older version:

                        * http://anonsvn.jboss.org/repos/jbossas/projects/jboss-deployers/trunk/deployers-vfs/src/main/java/org/jboss/deployers/vfs/plugins/classloader/VFSDeploymentClassLoaderPolicyModule.java

                        • 11. Re: Deadlock and infinite loop between BaseClassLoader and Java Policy Provider
                          dongguang zhou Newbie

                          In JDK 1.4 or later, it introduced a new constructor of ProtectionDomain to defer Policy.getPermissions(Codesource) until evaluating Java 2 policies in security contexts, see ProtectionDomain(CodeSource codesource, PermissionCollection permissions, ClassLoader classloader, Principal[] principals), which was desigend to support dynamic policy loading.

                           

                          With this approach, I created a custom ClassLoaderPolicy and replaced the default one in bootstrap/deployers.xml, and it solves the deadlock and infinite loop issue I have on JBoss. I think JBoss should take the advantage of this approach in the default ClassLoaderPolicy VFSClassLoaderPolicy, and support dynamical policy loading and refreshing.