On 26/02/2013 9:05 PM, Dan Creswell wrote:
On 26 February 2013 10:30, Peter Firmstone<[email protected]>  wrote:
Thanks Dan&  Gregg,

I've treated Permission object's as immutable, but that's clearly a poor
assumption, despite the Permission javadoc suggesting implementations should
be immutable, just shows you can't be too careful with other people's code.

To make an object effectively immutable (without final fields), it must be
safely published after construction, which means writing to a concurrent
collection, queue, final field, volatile field or a field protected by
synchronization for the hand over from one thread to the next, it cannot be
modified after publication either.

A truely immutable object has final fields, safe construction and can't be
modified, it's safely published after the constructor returns, so is more
robust as Gregg mentioned.
Mmm observation, there are two immutables:

(1) Immutable as perceived by the user of the object.
(2) Immutable internal implementation.

In this case, I've broken this rule, I've added SocketPermission to an
unsynchronized collection (protected from external modification) and that
collection is published to a final field in a constructor.
Final of course meaning nothing other than "value won't change" - a
compile/language level guarantee.

It doesn't interact in any meaningful way with the JVM concurrency
mechanics. i.e. it doesn't have any relationship to a synchronization
action (or barrier as I prefer). Thus final provides no multi-thread
guarantee explicitly. What one can say is that once the value becomes
visible to a set of threads it won't change.

Now this is where things get a little uncertain, this final field only
applies to the safe publication of the Collection, the Permission objects it
contains are not referenced by final fields nor synchronized so they're not
safely published, even though Permission objects are written to this
collection prior to the final field being initialised.  (At least that's
what I gather from reading Concurrency in practise).

I agree with that assessment.

So I'm going to have to take a minor performance hit.

There are two solutions:

   1. Use a concurrent collection instead (ConcurrentSkipListSet, which
      is a ConcurrentSkipListMap discuised as a Set)
   2. Wrap each Permission in an Object and safely publish the
      Permission to a final field, then add the wrapper to the
      collection, the other thread then accesses the final field
      ensuring safe publication.  The collection is still an
      unsynchronized collection, but now contains immutable objects and
      hasn't been changed since it was published.

Option 2 requires the creation of more temporary objects, but should still
avoid cache misses, while option 1 uses volatile variables to safely publish
the Permission's.  Since this collection isn't modified after construction
volatile reads will be almost as cheap as nonvolatile reads and I can use
the existing PermissionComparator.  I think I'll go with option 1.

+1 for option 1.

If you look at any security debugging output, remembering that many
duplicate checks are eliminated by CombinerSecurityManager, the security
architecture in a network environment gets absolutely hammered.

As an aside, do you know what the typical overall cost on CPU this
represents? Or another dimension: What additional latency might this
add to your "average" (loose, I know) method invocation?

Firstly pretend you've got a standard SecurityManager and Sun's PolicyFile.

It really depends on the permission being checked, if it's AllPermission, it's going to be very fast, but if it's a blocking Permission, like SocketPermission and there are 10 ProtectionDomain's on the call stack, (one for each jar file) and some of those ProtectionDomain's are granted more than one SocketPermission, (most admins have probably learned to keep this simple, but the proxy server origin has already been granted at ClassLoader & ProtectionDomain creation time, and the order's never how you want it, you might be waiting minutes for a DNS lookup for two dns hostnames, just to see if they've got identical IP addresses and that's just for one PD. So you're now blocking other security checks for that ProtectionDomain too, no AccessControlContext containing that PD is able to proceed until SocketPermission completes on that ProtectionDomain, even if they're trying to check other unrelated permissions.

To make matters worse, if the Policy fails, the ProtectionDomain is checked as well, so the same SocketPermission can be checked twice, although at least i won't lookup dns again.

To build your cache Sun's PolicyFile, uses CodeSource.implies, which also blocks on dns, because it uses URL.

Once it enters the dns cache you've still got contention for that (which is why we've included dnsjava).

With CombinerSecurityManager and ConcurrentPolicyFile, your SocketPermission's will be ordered dynamically for every permission check, so if you have a wildcard as well as dns names, the wild card will be checked first, unless you have an exact match, in which case SocketPermission.implies won't even be called.

If you've got 10 ProtectionDomain's on the stack, and the AccessControlContext doesn't already exist in the cache, a number of Runnable tasks are created and submitted to an Executor to perform checks in Parallel on each ProtectionDomain in that AccessControlContext. Some of these will return immediately, while those that block will probably be suspended by the OS allowing other non blocking threads to continue. DNS lookups will proceed in parallel instead of series when required, allowing other permission checks to avoid blocking.

A big advantage of a non blocking policy and security manager is deadlock elimination.

If you have multiple threads with identical AccessControlContext's calling SecurityMangager.checkPermission, then after a permission has been checked by the policy it's added to the SecurityManager's non blocking cache until the policy is refreshed. So other threads making the same call will perform a very fast hash lookup for their AccessControlContext, followed by a PermissionComparator based lookup on all the Permissions checked for that AccessControlContext. When AccessControlContext's are due to be garbage collected, they're removed from the cache by a background thread, Permission's that aren't used for 5 to 10 minutes are removed also by the same background thread.

When there are only a few ProtectionDomain's in an AccessControlContext, checks are executed in series. If there are no available Executor threads, the permission check is performed by the calling thread (to avoid deadlock).

CodeSource.implies is not utilised by ConcurrentPolicyFile, instead, CodeSource URL's are converted to URI, normalised and compared, this avoids dns lookups and disk access so is much faster.

So the bigger the system, the better it will perform, because the likelyhood of blocking increases.

This is an old although interesting paper on SecurityManager performance: http://rewerse.eu/publications/download/REWERSE-RP-2005-141.pdf

Creating the AccessControlContext from stack inspection is about half the cost.

Startup cost for ConcurrentPolicyFile and CombinerSecurityManager is greater.

I'd be interested to see what the results are like in a real djinn.

Cheers,

Peter.

Cheers,

Peter.

Reply via email to