JDK-6552236 : PolicyFile not synchronized during refresh
  • Type: Bug
  • Component: security-libs
  • Sub-Component: java.security
  • Affected Version: 6
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2007-05-01
  • Updated: 2011-05-02
  • Resolved: 2011-05-02
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6 JDK 7
5.0u19Fixed 6u10Fixed 7 b19Fixed
Description
sun.security.provider.PolicyFile assumes that the internal policy state is read-only (that it can not change once the policy data has been read in).  In a multi-threaded environment, however, asynchronous refreshes can affect policy state.

The following is a rudimentary way to reproduce the problem:

1. Change PolicyFile.getPermissions:

  private Permissions getPermissions(Permissions perms,
                                     final CodeSource cs,
                                     Principal[] principals) {
    int numEntries = policyInfo.policyEntries.size();
    try {
        Thread.currentThread().sleep(5000);
    } catch (Exception e) {
    }
    PolicyEntry entry;
    for (int i = 0; i < numEntries; i++) {

    // remainder is the same

2. Run this test program:

  import java.security.AllPermission;
  import java.security.Policy;

  public class Refresh extends Thread {

    private boolean refresh;    // call Policy.refresh versus Policy.implies

    public Refresh(boolean refresh) {
        this.refresh = refresh;
    }

    public void run() {
        if (refresh) {
            while (true) {
                Policy.getPolicy().refresh();
            }
        } else {
            while (true) {
                Policy.getPolicy().implies
                                (getClass().getProtectionDomain(),
                                new AllPermission());
            }
        }
    }

    public static void main(String[] args) throws Exception {
        Refresh implies = new Refresh(false);
        implies.start();

        Refresh refresh = new Refresh(true);
        refresh.start();
    }
  }

3. During the 5 seconds PolicyFile puts the "implies" thread to sleep, update:
  ~/lib/security/java.policy

  Remove one of the grant entries.
  Save the file.

4. Observe this exception:

  angeles(218):java Refresh
  Exception in thread "Thread-0" java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
     at java.util.ArrayList.RangeCheck(ArrayList.java:547)
     at java.util.ArrayList.get(ArrayList.java:322)
     at sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1261)
     at sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1223)
     at sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1165)
     at sun.security.provider.PolicyFile.implies(PolicyFile.java:1120)
     at Refresh.run(Refresh.java:19)

Comments
EVALUATION The race condition was caused by the code accessing different fields of the shared PolicyInfo object without locking it. During a refresh, another thread could replace the PolicyInfo object, causing unpredictable behavior. The fix I came up was to store the PolicyInfo in an AtomicReference object, which doesn't use locks. This fix performed much better than the suggested fix using locks, which caused a 5-10% drop in my tests with large number of threads (due to the lock contention). I also cleaned up the rest of the synch code a little: - I wrapped the aliasMapping and identityPolicyEntries fields of the PolicyInfo class in synchronized collections. This delegated the locking to the collections and I was able to remove some of the locking (synchronized blocks) in the PolicyFile code, although a lock is still needed when iterating over the entries.
03-08-2007

SUGGESTED FIX See attached webrev.refresh.zip for a very lightly tested fix. This fix has performance implications because causes more synchronization (contention) on a shared object. One potential alternative solution is to use a java.util.concurrent.lock.ReadWriteLock instead of a mutual exclusive lock. *** (#1 of 1): [ UNSAVED ] ###@###.###
01-05-2007