This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-rng.git

commit d01b5051799cd60f7b675e4ee1680bc35079b436
Author: aherbert <[email protected]>
AuthorDate: Tue Apr 5 13:26:04 2022 +0100

    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.
---
 .../rng/simple/internal/ProviderBuilder.java       | 29 +++++++++++++++++++++-
 .../commons/rng/simple/RandomSourceTest.java       | 19 ++++++++++++++
 src/changes/changes.xml                            |  4 +++
 3 files changed, 51 insertions(+), 1 deletion(-)

diff --git 
a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java
 
b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java
index 11399b9a..78e76548 100644
--- 
a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java
+++ 
b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ProviderBuilder.java
@@ -30,6 +30,7 @@ import org.apache.commons.rng.core.source32.Well19937c;
 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 @@ public final class ProviderBuilder {
 
             @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));
             }
 
             /**
diff --git 
a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/RandomSourceTest.java
 
b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/RandomSourceTest.java
index e5e7ea71..5bb0c8ef 100644
--- 
a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/RandomSourceTest.java
+++ 
b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/RandomSourceTest.java
@@ -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 @@ class RandomSourceTest {
         
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);
+        });
+    }
 }
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 0b0a08cf..f986aae4 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -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.

Reply via email to