I've been able to work around the SocketPermission equals and hashCode
problems for DelegatePermission (which may contain a SocketPermission),
by implementing equals and hashCode in the same way implemented by
Object (reference == ), then using a static factory method to ensure
there are no duplicates. Permission forces you to re-implement equals
and hashCode. This enables a DelegatePermission containing a
SocketPermission to be cached. The speedup on repeat permission checks
(20,000 calls) is a factor of 20. SocketPermission check results are
not cached by the SecurityManager, because of the broken equals and
hashCode behaviour.
I've also been able to split the ProtectionDomain's contained in the
AccessControlContext into separate permission checks using an
ExecutorService, which reduces the time taken to perform network DNS
lookups, since they execute in parallel, rather than series.
The Executor uses a formula to calculate the number of threads based on
the available CPU's:
double blocking_coefficient = 0.8; // 0 CPU intensive to 0.9 IO
intensive
int numberOfCores = Runtime.getRuntime().availableProcessors();
int poolSize = (int) (numberOfCores / ( 1 - blocking_coefficient));
On my computer with 4 cpu's, the executor uses a maximum of 20 threads
(this figure may require adjustment), it would calculate 160 threads on
a 32 cpu box.
If your DNS doesn't support reverse lookup's, for whatever reason, this
won't fix that problem.
I have decided to replace the use of CodeSource URL's with URI, in the
policy implementation, to eliminate DNS lookups there.
I'm undecided about reimplementing SocketPermission and
SocketPermissionCollection, it may be worth waiting to see if the
concurrent policy and executor based security manager are sufficient to
reduce the performance impact for most situations.
Downloaded proxy's that are successfully unmarshalled are automatically
given static Permissions in the ProtectionDomain constructor, but this
is a specific SocketPermission which requires a reverse DNS lookup, so
to improve performance after proxy verification, dynamically add a wild
card SocketPermission that won't require the reverse DNS lookup, the
policy will be consulted first and will return faster.
Only use wildcard SocketPermission's in policy files, this avoids
reverse DNS lookups, remove other types of SocketPermission's from
policy files and replace with wild cards, this may of course require an
authenticate Principal.
Change the default jvm policy file that contains localhost to use the IP
127.0.0.1 instead. I could actually do that in the policy parser I'm
writing, seeing as it happens so often!
grant {
<SNIP>
// allows anyone to listen on un-privileged ports
// permission java.net.SocketPermission "localhost:1024-", "listen";
permission java.net.SocketPermission "127.0.0.1:1024-", "listen";
<SNIP>
}
The PreferredClassProvider loader table that Chris was referring to with
LookupLocatorDiscovery and Reggie scalability uses a synchronized map
and URL's in it's key's, we could look into using URI based key's
instead, this is another source of DNS lookups.
URL is a particularly bad class to use as a key, due to the equals and
hashcode methods requiring DNS lookup. And each key in the loader table
uses an array of URL's, as in the key.
Cheers,
Peter.
Peter Firmstone wrote:
DNS lookups and reverse lookups caused by URL and SocketPermission,
equals, hashCode and implies methods create some serious performance
problems for distributed programs.
The concurrent policy implementation I've been working on reduces lock
contention between threads performing security checks.
When the SecurityManager is used to check a guard, it calls the
AccessController, which retrieves the AccessControlContext from the
call stack, this contains all the ProtectionDomain's on the call stack
(I won't go into privileged calls here), if a ProtectionDomain is
dynamic it will consult the Policy, prior to checking the static
permissions it contains.
The problem with the old policy implementation is lock contention
caused by multiple threads all using multiple ProtectionDomains, when
the time taken to perform a check is considerable, especially where
identical security checks might be performed by multiple threads
executing the same code.
Although concurrent policy reduces contention between
ProtectionDomain's calls to Policy.implies, there remain some
fundamental problems with the implementations of SocketPermission and
URL, that cause unnecessary DNS lookups during equals(), hashCode()
and implies() methods.
The following bugs concern SocketPermission (please read before
continuing) :
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6592285
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4975882 - contains
a lot of valuable comments.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4671007 - fixed,
perhaps incorrectly.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6501746
Anyway to cut a long story short, DNS lookups and DNS reverse lookups
are performed for the equals and hashCode implementations in
SocketPermission and URL, with disastrous performance implications for
policy implementations using collections and caching security
permission check results.
For example, once a SocketPermission guard has been checked for a
specific AccessContolContext the result is cached by my
SecurityManager, avoiding repeat security checks, however if that
cache contains SocketPermission, DNS lookups will be required, the
cache will perform slower than some other directly performed security
checks! The cache is intended to return quickly to avoid reconsulting
every ProtectionDomain on the stack.
To make matters worse, when checking a SocketPermission guard, the DNS
may be consulted for every non wild card SocketPermission contained
within a SocketPermissionCollection, up until it is implied. DNS
checks are being made unnecessarily, since the wild card that matches
may not require a DNS lookup at all, but because the non matching
SocketPermission's are being checked first, the DNS lookups and
reverse lookups are still performed. This could be fixed completely,
by moving the responsibility of DNS lookups from SocketPermission to
SocketPermissionCollection.
The identity of two SocketPermission's are equal if they resolve to
the same IP address, but their hashCode's are different! See bug 6592623.
The identity of a SocketPermission with an IP address and a DNS name,
resolving to identical IP address should not (in my opinion) be equal,
but is! One SocketPermission should only imply the other while DNS
resolves to the same IP address, otherwise the equality of the two
SocketPermission's will change if the IP address is assigned to a
different domain! Object equality / identity shouldn't depend on the
result of a possibly unreliable network source.
SocketPermission and SocketPermissionCollection are broken, the only
solution I can think of is to re-implement these classes (from
Harmony) in the policy and SecurityManager, substituting the existing
jvm classes. This would not be visible to client developers.
SocketPermission's may also exist in a ProtectionDomain's static
Permissions, these would have to be converted by the policy when
merging the permissions from the ProtectionDomain with those from the
policy. Since ProtectionDomain, attempts to check it's own internal
permissions, after the policy permission check fails, DNS checks are
currently performed by duplicate SocketPermission's residing in the
ProectionDomain, this will no longer occur, since the permission being
checked will be converted to say for argument sake
org.apache.river.security.SocketPermission. However because some
ProtectionDomains are static, they never consult the policy, so the
Permission's contained in each ProtectionDomain will require
conversion also, to do so will require extending and implementing a
ProtectionDomain that encapsulates existing ProtectionDomain's in the
AccessControlContext, by utilising a DomainCombiner.
For CodeSource grant's, the policy file based grant's are defined by
URL's, however URL's identity depend upon DNS record results, similar
to SocketPermission equals and hashCode implementations which we have
no control over.
I'm thinking about implementing URI based grant's instead, to avoid
DNS lookups, then allowing a policy compatibility mode to be enabled
(with logging) for falling back to CodeSource grant's when a URL
cannot be converted to a URI, this is a much simpler fix than the
SocketPermission problem.
For Dynamic Policy Grants, because ProtectionDomain doesn't override
equals (that's a good thing), the contained CodeSource must also be
checked, again potentially slowing down permission checks with DNS
lookups, simply because CodeSource uses URL's. Changing the Dynamic
Grant's to use URI based comparison would be relatively simple, since
the URI is obtained dynamically when the dynamic grant is created.
URI based grant's don't use DNS resolution and would have a narrower
scope of implied CodeSources, an IP based grant won't imply a DNS
domain URL based CodeSource and vice versa. Rather than rely on DNS
resolution, grant's could be made specifically for IPv4, IPv6 and DNS
names in policy files. URL.toURI() can be utilised to check if URI
grant's imply a CodeSource without resorting to DNS.
Any thoughts, comments or ideas?
N.B. It's sad that security is implemented the way it is, it would be
far better if it was Executor based, since every protection domain
could be checked in parallel, rather than in sequence.
Regards,
Peter.