[concurrency-interest] ThreadLocalRandom clinit troubles

Peter Levart peter.levart at gmail.com
Wed Jun 25 13:41:57 EDT 2014


To sum-up: We have a problem with TLR initialization since by default it 
uses networking code to compute initial "seeder" value which can execute 
user code in at least two situations:

- when "sun.net.spi.nameservice.provider" system property is defined to 
use custom NameService provider
- when custom SecurityManager is in effect while TLR is being initialized
- also, when "java.util.secureRandomSeed" is defined, at least on 
Windows this means that default SecureRandom algorithm will be using 
networking code too to gather system entropy

We could work-around these problems by delaying initialization of 
NameService provider(s), by-passing SecurityManager when obtaining MAC 
address from NetworkInterface and extending the semantics of 
"java.util.secureRandomSeed" property to specify explicit SecureRandom 
algorithm and provider to use when obtaining SecureRandom instance, like 
in the following patch:

http://cr.openjdk.java.net/~plevart/jdk9-dev/TLR.initialSeed/webrev.01/

But on the other hand this seems too many knobs to worry about. Ideally 
one would like to always use OS provided native seed source, but 
SecureRandom (with all the security providers infrastructure) seems too 
heavy-weight to be used in classes like ThreadLocalRandom or 
SplittableRandom by default since they can be initialized very early in 
the start-up sequence. I made an experiment with class-loading. Recent 
JDK9 build loads 381 classes when running the following empty program on 
Linux:

public class test0 {
         public static void main(String[] args) {
         }
}

...ThreadLocalRandom is not among them. But in special configurations 
(like when using java agents) or in the future, it could be. The 
following program:

public class test {
         public static void main(String[] args) {
                 java.util.concurrent.ThreadLocalRandom.current();
         }
}

...loads 403 classes. That's 22 more than an empty program. The 
following classes are loaded in addition:

+ java.util.Random
+ java.util.concurrent.ThreadLocalRandom
+ java.net.NetworkInterface
+ java.net.NetworkInterface$1
+ java.net.InterfaceAddress
+ java.net.InetAddress
+ sun.security.action.GetBooleanAction
+ java.net.InetAddress$1
+ java.net.InetAddress$InetAddressHolder
+ java.net.InetAddress$Cache
+ java.net.InetAddress$Cache$Type
+ java.net.InetAddressImplFactory
+ java.net.InetAddressImpl
+ java.net.Inet6AddressImpl
+ sun.net.spi.nameservice.NameService
+ java.net.InetAddress$2
+ java.net.Inet4Address
+ java.net.Inet6Address
+ java.net.Inet6Address$Inet6AddressHolder
+ java.net.DefaultInterface
+ java.net.NetworkInterface$2
+ sun.nio.ch.Interruptible

If I run the same program but set the "java.util.secureRandomSeed=true", 
it loads 435 classes. Besides 381 classes loaded by an empty program, 
the following 54 classes are loaded in addition:

+ java.util.Random
+ java.util.concurrent.ThreadLocalRandom
+ java.security.SecureRandom
+ sun.security.jca.Providers
+ java.lang.InheritableThreadLocal
+ sun.security.jca.ProviderList
+ sun.security.jca.ProviderConfig
+ java.security.Provider
+ sun.security.jca.ProviderList$3
+ sun.security.jca.ProviderList$1
+ java.security.Provider$ServiceKey
+ java.security.Provider$EngineDescription
+ sun.misc.FloatingDecimal
+ sun.misc.FloatingDecimal$BinaryToASCIIConverter
+ sun.misc.FloatingDecimal$ExceptionalBinaryToASCIIBuffer
+ sun.misc.FloatingDecimal$BinaryToASCIIBuffer
+ sun.misc.FloatingDecimal$1
+ sun.misc.FloatingDecimal$ASCIIToBinaryConverter
+ sun.misc.FloatingDecimal$PreparedASCIIToBinaryBuffer
+ sun.misc.FDBigInteger
+ sun.security.jca.ProviderList$2
+ java.security.Security
+ java.security.Security$1
+ java.util.Properties$LineReader
+ java.util.AbstractList$Itr
+ sun.security.jca.ProviderConfig$2
+ sun.security.provider.Sun
+ sun.security.provider.SunEntries
+ sun.security.provider.SunEntries$1
+ java.security.SecureRandomSpi
+ sun.security.provider.NativePRNG
+ sun.security.provider.NativePRNG$Variant
+ sun.security.provider.NativePRNG$1
+ sun.security.provider.NativePRNG$2
+ java.net.URI
+ java.net.URI$Parser
+ sun.security.provider.NativePRNG$RandomIO
+ sun.security.provider.NativePRNG$Blocking
+ sun.security.provider.NativePRNG$NonBlocking
+ java.util.LinkedHashMap$LinkedEntrySet
+ java.util.LinkedHashMap$LinkedHashIterator
+ java.util.LinkedHashMap$LinkedEntryIterator
+ java.security.Provider$Service
+ java.security.Provider$UString
+ java.util.LinkedHashSet
+ java.util.LinkedHashMap$LinkedValues
+ java.util.LinkedHashMap$LinkedValueIterator
+ java.util.Collections$UnmodifiableSet
+ java.util.Collections$UnmodifiableCollection$1
+ java.util.LinkedHashMap$LinkedKeySet
+ java.util.LinkedHashMap$LinkedKeyIterator
+ sun.security.jca.GetInstance
+ sun.security.jca.GetInstance$Instance
+ sun.nio.ch.Interruptible


This seems too heavy-weight even if the initialization issue on Windows 
where default SecureRandom algorithm is using networking code to gather 
system entropy is worked-around by requesting explicit "Windows-PRNG" 
SecureRandom algorithm from "SunMSCAPI" provider.

Peeking around in the sun.security.provider package, I found there 
already is a minimal internal infrastructure for obtaining random seed. 
It's encapsulated in package-private abstract class 
sun.security.provider.SeedGenerator with 4 implementations. It turns out 
that, besides Java-only fall-back implementation called 
ThreadedSeedGenerator and generic URLSeedGenerator, there are also two 
implementations of NativeSeedGenerator (one for UNIX-es which is just an 
extension of URLSeedGenerator and the other for Windows which uses MS 
CryptoAPI). I made a few tweaks that allow this sub-infrastructure to be 
used directly in ThreadLocalRandom and SplittableRandom:

http://cr.openjdk.java.net/~plevart/jdk9-dev/TLR_SR_SeedGenerator/webrev.01/

The changes are as follows:
- modified SeedGenerator to be a public class (was package-private as 
are still all it's subclasses)
- made its only public static method package-private (was public for no 
reason)
- made its only abstract method public (was package-private)
- made SeedGenerator implement AutoCloseable with empty close() method 
(overriden in URLSeedGenerator to close the underlying stream)
- added public static factory method to return a new instance of 
NativeSeedGenerator (using /dev/urandom on UNIX-es, MSCAPI on Windows) 
and protected it with a runtime check so that it can only be used internally

With these changes and modified TLR, running the test program (see 
above) loads only the following 15 additional classes besides those that 
are loaded by an empty program on Linux (and I assume the number is the 
same on Windows):


+ java.util.Random
+ java.util.concurrent.ThreadLocalRandom
+ sun.security.provider.SeedGenerator
+ sun.security.provider.SunEntries
+ sun.security.provider.SunEntries$1
+ java.security.Security
+ java.security.Security$1
+ java.util.Properties$LineReader
+ sun.security.provider.SeedGenerator$URLSeedGenerator
+ sun.security.provider.NativeSeedGenerator
+ sun.security.provider.SeedGenerator$1
+ sun.security.provider.SeedGenerator$URLSeedGenerator$1
+ java.net.URI
+ java.net.URI$Parser
+ sun.nio.ch.Interruptible


So what do you think is the best direction to go further with this? 
Patching networking or exposing NativeSeedGenerator to internal JDK code?


Regards, Peter

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20140625/9734239b/attachment.html>


More information about the Concurrency-interest mailing list