There are some pretty important dots missing here.

NativePRNG$RandomIO.implNextBytes() does unfortunately use a global lock, and this method gets called every time SecureRandom is used to generate more bytes. So one can imagine if lots of threads want to do this, the lock gets contended and slows down.

But NativePRNG$RandomIO.implNextBytes() doesn't use /dev/random by itself. It delegates to sun.security.provider.SecureRandom#engineNextBytes() (which is what getMixRandom() returns), then it reads from /dev/urandom (happen in ensureBufferValid) and XOR bytes.

// get pseudo random bytes
// read from /dev/urandom and XOR with bytes generated by the
// mixing SHA1PRNG
private void implNextBytes(byte[] data) {
    synchronized (LOCK_GET_BYTES) {
        try {
            getMixRandom().engineNextBytes(data);
            int len = data.length;
            int ofs = 0;
            while (len > 0) {
                ensureBufferValid();
                int bufferOfs = urandomBuffer.length - buffered;
                while ((len > 0) && (buffered > 0)) {
                    data[ofs++] ^= urandomBuffer[bufferOfs++];
                    len--;
                    buffered--;
                }
            }
        } catch (IOException e) {
            throw new ProviderException("nextBytes() failed", e);
        }
    }
}

And sun.security.provider.SecureRandom#engineNextBytes() only uses seeder.engineNextBytes() once to create the initial hidden state, which means in the call path of NativePRNG.engineNextBytes() this only happens once in JVM. The rest is just SHA1 digest computation:

/**
 * Generates a user-specified number of random bytes.
 *
 * @param bytes the array to be filled in with random bytes.
 */
public synchronized void engineNextBytes(byte[] result) {
    int index = 0;
    int todo;
    byte[] output = remainder;

    if (state == null) {
        byte[] seed = new byte[DIGEST_SIZE];
        SeederHolder.seeder.engineNextBytes(seed);
        state = digest.digest(seed);
    }

    ...
}

So whatever that seeder does (such as possibly accessing /dev/random and blocking), it can't impact the overall performance once!

What seems to be actually happening is that the {{-Djava.security.egd=file:/dev/./urandom}} property causes SecureRandomSpi to switch to the instance of sun.security.provider.SecureRandom, not sun.security.provider.NativePRNG, which entirely bypasses reading /dev/urandom and just go straight to SHA1 computation. This happens in sun.security.provider.SunEntries

static void putEntries(Map<Object, Object> map) {

    /*
     * SecureRandom
     *
     * Register these first to speed up "new SecureRandom()",
     * which iterates through the list of algorithms
     */
    // register the native PRNG, if available
    // if user selected /dev/urandom, we put it before SHA1PRNG,
    // otherwise after it
    boolean nativeAvailable = NativePRNG.isAvailable();
    boolean useUrandom = seedSource.equals(URL_DEV_URANDOM);
    if (nativeAvailable && useUrandom) {
        map.put("SecureRandom.NativePRNG",
            "sun.security.provider.NativePRNG");
    }
    map.put("SecureRandom.SHA1PRNG",
         "sun.security.provider.SecureRandom");
    if (nativeAvailable && !useUrandom) {
        map.put("SecureRandom.NativePRNG",
            "sun.security.provider.NativePRNG");
    }

You can see the effect of this in the following simple program:

public class Foo {
    public static void main(String[] args) throws Exception {
        SecureRandom s = new SecureRandom();
        System.out.println(s.getProvider());

        Field f = s.getClass().getDeclaredField("secureRandomSpi");
        f.setAccessible(true);
        Object spi = f.get(s);
        System.out.println(spi);
    }
}
java.security.egd=file:/dev/./urandom
SUN version 1.6
sun.security.provider.SecureRandom@398020cc

java.security.egd=null
SUN version 1.6
sun.security.provider.NativePRNG@2bf14ceb

All this is doing is to bypass a global lock (and stop using /dev/urandom altogether), and yes, I can definitely see why that is a boon to the performance, but this has nothing to do with /dev/random blocking. The switch doesn't cause JVM to use /dev/urandom instead of /dev/random. It just bypasses both.

This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators.
For more information on JIRA, see: http://www.atlassian.com/software/jira

--
You received this message because you are subscribed to the Google Groups "Jenkins Issues" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jenkinsci-issues+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to