Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8282144

RandomSupport.convertSeedBytesToLongs sign extension overwrites previous bytes

XMLWordPrintable

    • b13
    • generic
    • generic
    • Verified

        A DESCRIPTION OF THE PROBLEM :
        Class: ./java.base/share/classes/jdk/internal/util/random/RandomSupport.java
        Method: public static long[] convertSeedBytesToLongs(byte[] seed, int n, int z)

        The method attempts to create an array of longs by consuming the input bytes most significant bit first. New bytes are appended to the existing long using the OR operator on the signed byte. Due to sign extension this will overwrite all the existing bits from 63 to 8 if the next byte is negative.


        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        The bug will not readily manifest as a poorly seeded RNG with many of the bits in the generated long[] set to 1 quickly recovers and outputs a random sequence. Reproducing cannot be done without access to the internal class RandomSupport.

        I have extracted the part of the class with the bug into a driver program:

        public class RandomSupport {
          private static final char[] HEX_DIGITS =
              {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

          public static long[] convertSeedBytesToLongs(byte[] seed, int n, int z) {
            final long[] result = new long[n];
            final int m = Math.min(seed.length, n << 3);
            // Distribute seed bytes into the words to be formed.
            for (int j = 0; j < m; j++) {
              // Sign extension bug
              result[j >> 3] = (result[j >> 3] << 8) | seed[j];
            }
            // Filling the rest of the long[] has been removed for brevity.
            // It only matters if the bytes are shorter than the desired length of long[].
            return result;
          }

          public static long[] convertSeedBytesToLongsFixed(byte[] seed, int n, int z) {
            final long[] result = new long[n];
            final int m = Math.min(seed.length, n << 3);
            // Distribute seed bytes into the words to be formed.
            for (int j = 0; j < m; j++) {
              result[j >> 3] = (result[j >> 3] << 8) | (seed[j] & 0xff);
            }
            return result;
          }

          public static void main(String[] args) {
            RandomGenerator rng = RandomGeneratorFactory.of("L64X128MixRandom").create(42);
            for (int i = 1; i < 8; i++) {
              byte[] seed = new byte[i];
              for (int j = 0; j < 10; j++) {
                rng.nextBytes(seed);

                for (byte b : seed) {
                  System.out.printf("%c%c", HEX_DIGITS[(b & 0xf0) >> 4], HEX_DIGITS[b & 0xf]);
                }
                System.out.printf(" %-16s %-16s%n",
                    Long.toHexString(convertSeedBytesToLongs(seed, 1, 1)[0]),
                    Long.toHexString(convertSeedBytesToLongsFixed(seed, 1, 1)[0]));
              }
            }
          }
        }


        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        Compile and run the driver program:

        > javac RandomSupport.java
        > java RandomSupport

        Column: Description
        1: Hex representation of the input byte[], most significant bits first
        2: Current long output (hex representation)
        3: Corrected long output (hex representation)

        12 12 12
        10 10 10
        38 38 38
        ec ffffffffffffffec ec
        8a ffffffffffffff8a 8a
        7e 7e 7e
        cf ffffffffffffffcf cf
        12 12 12
        11 11 11
        d4 ffffffffffffffd4 d4
        7ae2 ffffffffffffffe2 7ae2
        47c8 ffffffffffffffc8 47c8
        3cc0 ffffffffffffffc0 3cc0
        2475 2475 2475
        fdce ffffffffffffffce fdce
        7c04 7c04 7c04
        16a4 ffffffffffffffa4 16a4
        0bf1 fffffffffffffff1 bf1
        cb22 ffffffffffffcb22 cb22
        63eb ffffffffffffffeb 63eb
        6d72b7 ffffffffffffffb7 6d72b7
        ddd3a6 ffffffffffffffa6 ddd3a6
        6194a2 ffffffffffffffa2 6194a2
        d54bc0 ffffffffffffffc0 d54bc0
        3142f5 fffffffffffffff5 3142f5
        643056 643056 643056
        ff94ba ffffffffffffffba ff94ba
        86923e ffffffffffff923e 86923e
        faf8f0 fffffffffffffff0 faf8f0
        b69017 ffffffffffff9017 b69017
        f4b40258 ffffffffffb40258 f4b40258
        c099cae2 ffffffffffffffe2 c099cae2
        9273a475 ffffffffffffa475 9273a475
        99359c54 ffffffffffff9c54 99359c54
        c4bc9ec4 ffffffffffffffc4 c4bc9ec4
        b6fe22ae ffffffffffffffae b6fe22ae
        45da4c38 ffffffffffda4c38 45da4c38
        c88f5faf ffffffffffffffaf c88f5faf
        7e45efe8 ffffffffffffffe8 7e45efe8
        94c41ade ffffffffffffffde 94c41ade
        b4e3c54f86 ffffffffffffff86 b4e3c54f86
        b60419a301 ffffffffffffa301 b60419a301
        59c748bb7e ffffffffffffbb7e 59c748bb7e
        649d200092 ffffffffffffff92 649d200092
        1b92cd6d7d ffffffffffcd6d7d 1b92cd6d7d
        a53b475f48 ffffffa53b475f48 a53b475f48
        08437a5639 8437a5639 8437a5639
        7957b2be40 ffffffffffffbe40 7957b2be40
        2c725e4ba9 ffffffffffffffa9 2c725e4ba9
        f8e20015f8 fffffffffffffff8 f8e20015f8
        61647952a3cf ffffffffffffffcf 61647952a3cf
        c1a63d44671c ffffffa63d44671c c1a63d44671c
        08f4f39fa3cd ffffffffffffffcd 8f4f39fa3cd
        627a1e5efe40 fffffffffffffe40 627a1e5efe40
        60971cf0fa75 fffffffffffffa75 60971cf0fa75
        d4b76b20627c ffffffb76b20627c d4b76b20627c
        ad3590466c4a ffffffff90466c4a ad3590466c4a
        3d31e711953c ffffffffffff953c 3d31e711953c
        71b26c7c8c98 ffffffffffffff98 71b26c7c8c98
        cbadc78bf991 ffffffffffffff91 cbadc78bf991
        b7aba9f5d8b006 ffffffffffffb006 b7aba9f5d8b006
        e3da732bbdcc12 ffffffffffffcc12 e3da732bbdcc12
        7c3919405eb1ac ffffffffffffffac 7c3919405eb1ac
        5677bf994db54d ffffffffffffb54d 5677bf994db54d
        e06cb024107afd fffffffffffffffd e06cb024107afd
        65ff73250a89f7 fffffffffffffff7 65ff73250a89f7
        abb44e062013d2 ffffffffffffffd2 abb44e062013d2
        9f938d8a2c2239 ffffffff8a2c2239 9f938d8a2c2239
        375e554d2506c8 ffffffffffffffc8 375e554d2506c8
        90976dbbcf1cb2 ffffffffffffffb2 90976dbbcf1cb2

        ACTUAL -
        See expected result. The long created by the current code has many bits of the original input bytes overwritten with 1s (the output hex representation contains a lot of leading f characters). This results in loss of information in byte[] seeds.

        ---------- BEGIN SOURCE ----------
        See 'Steps to reproduce'
        ---------- END SOURCE ----------

        CUSTOMER SUBMITTED WORKAROUND :
        None. Currently any generator created using a random byte[] will not be seeded with the full randomness of the bytes. The only other method to create generators with a RandomGeneratorFactory uses a single long seed. Thus generators can only be created with 64-bits of randomness. Using a random byte[] will generate a long[] array for seeding where only the least significant 8-bits of each long are ensured to be random. The more significant bits have an increasing chance of being set to 1s. This is 1 - 1/(2^7) = 0.9921875 for the highest 8-bits in the long if 8 bytes were used to create it (as it has 7 chances to overwrite the bits as they are shifted up).

              jlaskey Jim Laskey
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              7 Start watching this issue

                Created:
                Updated:
                Resolved: