JDK-5034011 : ProtectionDomain.toString() in Policy.implies() causes StackOverflowError
  • Type: Bug
  • Component: security-libs
  • Sub-Component: java.security
  • Affected Version: 1.4.2
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: linux
  • CPU: x86
  • Submitted: 2004-04-19
  • Updated: 2004-04-21
  • Resolved: 2004-04-21
Related Reports
Relates :  
Description
Name: js151677			Date: 04/19/2004


FULL PRODUCT VERSION :
java version "1.4.2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2-b28)
Java HotSpot(TM) Client VM (build 1.4.2-b28, mixed mode)

java version "1.5.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b32c)
Java HotSpot(TM) Client VM (build 1.5.0-beta-b32c, mixed mode)

FULL OS VERSION :
SuSE Linux 7.1 (i386)
VERSION = 7.1
Kernel: 2.2.18

Can be reproducible on WindowsXP,Solaris.

A DESCRIPTION OF THE PROBLEM :
The cause of the problem is a call to ProtectionDomain.toString() inside the method implies() of a custom java.security.Policy implementation. If this method returns false, then an infinite recursion starts:

ProtectionDomain.toString() calls ProtectionDomain.seeAllp() which in turn checks permissions and thus ends up in calling Policy.implies().

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Compile PolicyTest.java
2. Put PolicyTest.class and test.policy into one directory
3. run:
   java -Djava.security.manager -Djava.security.auth.policy=test.policy PolicyTest

wait for the StackOverflowError.

Alternatively you can run this through a debugger, putting breakpoints at lines 17 and 28 (17: System.getProperty(), 28: ProtectionDomain.toString()) of PolicyTest.java.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Of course, an empty Policy implementation makes no sense, but I am planning to implement a custom Policy so I will need logging information. Calling toString() is quite natural for that purpose so it should be fail-safe in any case.
ACTUAL -
The result is a recursion between ProtectionDomain.toString() and Policy.implies() (and other methods inbetween).

ERROR MESSAGES/STACK TRACES THAT OCCUR :
This is the output from the debugger showing the stack with one recursion:

implies():28, PolicyTest$EmptyPolicy
implies():189, java.security.ProtectionDomain
checkPermission():254, java.security.AccessControlContext
checkPermission():401, java.security.AccessController
checkPermission():524, java.lang.SecurityManager
seeAllp():241, java.security.ProtectionDomain
toString():220, java.security.ProtectionDomain
valueOf():2131, java.lang.String
append():361, java.lang.StringBuffer
implies():28, PolicyTest$EmptyPolicy
implies():189, java.security.ProtectionDomain
checkPermission():254, java.security.AccessControlContext
checkPermission():401, java.security.AccessController
checkPermission():524, java.lang.SecurityManager
checkPropertyAccess():1276, java.lang.SecurityManager
getProperty():573, java.lang.System
main():17, PolicyTest

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
You need PolicyTest.java and test.policy:

/*
 * PolicyTest.java
 */
import java.security.*;
import java.io.*;

/**
 * Calling ProtectionDomain.toString() on a Policy that returns false from
 * implies(), causes a stack overflow.
 */
public class PolicyTest
{
    public static void main(String[] args) throws Exception
    {
        EmptyPolicy.setPolicy(new EmptyPolicy());
        System.out.println("Installed empty policy");
        System.out.println("Your home directory is " + System.getProperty("user.home"));
    }

    static class EmptyPolicy extends Policy
    {
        public EmptyPolicy()
        {
        }

        public boolean implies(ProtectionDomain domain, Permission permission)
        {
            System.out.println("implies called with ProtectionDomain " + domain + " Permission " + permission);
            //return deferredPolicy.implies(domain, permission);
            return false;
        }

        public PermissionCollection getPermissions(CodeSource codesource)
        {
            System.out.println("getPermissions called for codesource " + codesource);
            //PermissionCollection pc = deferredPolicy.getPermissions(codesource);
            System.out.println("returning " + null);
            return null;
        }

        public PermissionCollection getPermissions(ProtectionDomain domain)
        {
            System.out.println("getPermissions called for ProtectionDomain " + domain);
            //PermissionCollection pc = deferredPolicy.getPermissions(domain);
            System.out.println("returning " + null);
            return null;
        }

        public void refresh()
        {
            System.out.println("refreshing policy (no implementation)");
        }
    }
}

--------------------------------------------------------------------------

/*
 * test.policy
 */
grant codeBase "file:."
{
	permission java.security.SecurityPermission "setPolicy";
};
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Don't call ProtectionDomain.toString():

public boolean implies(ProtectionDomain domain, Permission permission)
{
     // causes StackOverflowError:
     //System.out.println("ProtectionDomain " + domain
     //                   + " Permission " + permission);
     System.out.println("ProtectionDomain " + domain.getCodeSource()
                        + " Permission " + permission);
     return false;
}
(Incident Review ID: 191392) 
======================================================================

Comments
PUBLIC COMMENTS A custom policy implementation should call AccessController.doPrivileged before the implementation itself performs a sensitive task (one that triggers a SecurityException). For example, if the policy implementation needs to read a file or open a socket to perform its work, it should make such calls inside a doPrivileged to eliminate all application code off the access control stack. Since printing a ProtectionDomain causes a security check, the implementation should likewise call ProtectionDomain.toString inside of a doPrivileged. Next, the custom policy implementation must recognize its own ProtectionDomain. If Policy.implies(domain, permission) is called and 'domain' is the domain of the implementation itself, then the implementation must recognize this and return 'true'. Otherwise the thread will loop endlessly. ###@###.### 2004-04-20
20-04-2004

EVALUATION see public summary ###@###.### 2004-04-20
20-04-2004