On Mon, 15 Mar 2021 22:11:51 GMT, SalusaSecondus
<[email protected]> wrote:
>> Cipher and KeyPairGenerator could be used a lot in TLS context. The
>> synchronization may impact in some circumstances. Supporting dynamic
>> configuration could be error-prone. For example, one thread to set the
>> providers, and another thread get the providers by calling Cipher.init().
>> Unless the set and get use the same lock, the return value of the get method
>> is not predictable. The lock here is on CachedSecureRandomHolder for
>> providers configuration getting, and no lock (or different lock) for
>> providers configuration setting. So applications may be able to get the
>> expected behaviors by introducing this synchronization.
>>
>> As this behavior has been a while, I may not update the implementation.
>> Instead, I would like to add an "implNote" tag to state that once the
>> default provider is loaded, an implementation may be changed it any longer.
>> Applications could use provider parameter if there is a preference.
>>
>> If you want go ahead, maybe the performance impacted could be mitigated by
>> locking less processes. For example:
>>
>> if (checkConfig) {
>> Provider[] currConfig = Security.getProviders();
>> if (!Arrays.equals(cachedConfig, currConfig)) {
>> synchronized (CachedSecureRandomHolder.class) {
>> // double check the currConfig
>> currConfig = Security.getProviders();
>> if (!Arrays.equals(cachedConfig, currConfig)) {
>> instance = ...
>> cachedConfig = ...
>> }
>> }
>> }
>> }
>> return instance;
>>
>> However, I still not very sure of the performance of Security.getProviders()
>> because it is synchronized as well. Further, I may want to evaluate if the
>> update of Security provider (for example Providers.setProviderList) could
>> trigger an even to update the default instances.
>
> I would definitely not call `new SecureRandom()` each time as that can have
> real performance problems (especially on systems which pull from
> `/dev/random` and may not have hardware entropy). The synchronization worries
> me too.
>
> Could `java.security.Security` contain a "provider version" `int` (volatile,
> atomic, or similar) which is incremented every time the provider list is
> changed? Then this class (clearly using some non-public API) would check that
> value and if it is the same, just return the cached instance. If it has
> changed since the last time, then it would update `instance` and its
> remembered value of the index. I think that we might be able to do it without
> taking any locks in this class because even if the providers change out from
> under us, we returned a reasonable value (subject to multi-threading) and
> will fix things up when we're called the next time.
>
> // using this and name collision to highlight what's a field and what's local
> int providerListVersion = Security.nonPublicGetProviderListVersion();
> if (this.providerListVersion != providerListVersion) {
> this.providerListVersion = providerListVersion;
> this.instance = new SecureRandom();
> }
> return this.instance;
Or, I can also separate out the instance returned by getSecureRandom() so that
code path should be unaffected, e.g.:
// cached SecureRandom instance
private static class CachedSecureRandomHolder {
public static SecureRandom instance = new SecureRandom();
private static Provider[] cachedConfig = Security.getProviders();
private static SecureRandom def = instance;
public static SecureRandom getDefault() {
synchronized (CachedSecureRandomHolder.class) {
Provider[] currConfig = Security.getProviders();
if (!Arrays.equals(cachedConfig, currConfig)) {
def = new SecureRandom();
cachedConfig = currConfig;
}
}
return def;
}
}
/**
* Get a SecureRandom instance. This method should be used by JDK
* internal code in favor of calling "new SecureRandom()". That needs to
* iterate through the provider table to find the default SecureRandom
* implementation, which is fairly inefficient.
*/
public static SecureRandom getSecureRandom() {
return CachedSecureRandomHolder.instance;
}
/**
* Get the default SecureRandom instance. This method is the
* optimized version of "new SecureRandom()" which re-uses the default
* SecureRandom impl if the provider table is the same.
*/
public static SecureRandom getDefSecureRandom() {
return CachedSecureRandomHolder.getDefault();
}
-------------
PR: https://git.openjdk.java.net/jdk/pull/3018