Skip to content
Permalink
Browse files
RNG-175: Fix MSWS createSeed(UniformRandomProvider) to handle a bad RNG
The createSeed(UniformRandomProvider) method should generate a seed even
if the input RNG is non-functional. This fixes an infinite loop when the
RNG output is not suitably random to create a seed.
  • Loading branch information
aherbert committed Apr 5, 2022
1 parent d340b96 commit d01b5051799cd60f7b675e4ee1680bc35079b436
Showing 3 changed files with 51 additions and 1 deletion.
@@ -30,6 +30,7 @@
import org.apache.commons.rng.core.source32.Well44497a;
import org.apache.commons.rng.core.source32.Well44497b;
import org.apache.commons.rng.core.source32.ISAACRandom;
import org.apache.commons.rng.core.source32.IntProvider;
import org.apache.commons.rng.core.source32.MersenneTwister;
import org.apache.commons.rng.core.source32.MiddleSquareWeylSequence;
import org.apache.commons.rng.core.source32.MultiplyWithCarry256;
@@ -281,7 +282,33 @@ protected Object convertSeed(Object seed) {

@Override
protected byte[] createByteArraySeed(UniformRandomProvider source) {
return NativeSeedType.convertSeedToBytes(createMswsSeed(source));
// The seed requires approximately 4-6 calls to nextInt().
// Wrap the input and switch to a default if the input is faulty.
final UniformRandomProvider wrapped = new IntProvider() {
/** The number of remaining calls to the source generator. */
private int calls = 100;
/** Default generator, initialised when required. */
private UniformRandomProvider defaultGen;
@Override
public int next() {
if (calls == 0) {
// The input source is broken.
// Seed a default
if (defaultGen == null) {
defaultGen = new SplitMix64(source.nextLong());
}
return defaultGen.nextInt();
}
calls--;
return source.nextInt();
}
@Override
public long nextLong() {
// No specific requirements so always use the source
return source.nextLong();
}
};
return NativeSeedType.convertSeedToBytes(createMswsSeed(wrapped));
}

/**
@@ -16,6 +16,8 @@
*/
package org.apache.commons.rng.simple;

import java.time.Duration;
import org.apache.commons.rng.core.source64.LongProvider;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

@@ -80,4 +82,21 @@ void testIsLongJumpable() {
Assertions.assertFalse(RandomSource.XOR_SHIFT_1024_S_PHI.isLongJumpable(), "XOR_SHIFT_1024_S_PHI is not LongJumpable");
Assertions.assertTrue(RandomSource.XO_SHI_RO_256_SS.isLongJumpable(), "XO_SHI_RO_256_SS is LongJumpable");
}

/**
* MSWS should not infinite loop if the input RNG fails to provide randomness to create a seed.
* See RNG-175.
*/
@Test
void testMSWSCreateSeed() {
final LongProvider broken = new LongProvider() {
@Override
public long next() {
return 0;
}
};
Assertions.assertTimeoutPreemptively(Duration.ofMillis(100), () -> {
RandomSource.MSWS.createSeed(broken);
});
}
}
@@ -86,6 +86,10 @@ behavioural compatibility between releases; derived types
may break behavioural compatibility. Any functional changes
will be recorded in the release notes.
">
<action dev="aherbert" type="fix" issue="175">
"RandomSource.MSWS": createSeed(UniformRandomProvider) to handle a bad RNG.
This fixes an infinite loop when the RNG output is not suitably random to create a seed.
</action>
<action dev="aherbert" type="add" issue="173">
"BaseProvider": Add a static method to extend input int[] and long[] seeds to a
minimum length.

0 comments on commit d01b505

Please sign in to comment.