Skip to content

Commit

Permalink
Linux: Upgrade random_get_pseudo_bytes() to xoshiro256++ 1.0
Browse files Browse the repository at this point in the history
The motivation for upgrading our PRNG is the recent buildbot failures in
the ZTS' tests/functional/fault/decompress_fault test. The probability
of a failure in that test is 0.8^256, which is ~1.6e-25 out of 1, yet we
have observed multiple test failures in it. This suggests a problem with
our random number generation.

The xorshift128+ generator that we were using has been replaced by newer
generators that have "better statistical properties". Some of the newer
random number generators are used in java.util.random as of Java 17.
After examining the choices, I have decided to replace the current PRNG
with xoshiro256++ 1.0.

It is hoped that this will fix the test failures in
tests/functional/fault/decompress_fault, although I have not done
simulations. I am skeptical that any simulations I do on a PRNG with a
period of 2^256 - 1 would be meaningful.

Since we have raised the minimum kernel version to 3.10 since this was
first implemented, we have the option of using the Linux kernel's
get_random_int(). However, I am not currently prepared to do performance
tests to ensure that this would not be a regression (for the time
being), so we opt for upgrading our PRNG to a newer one from Sebastiano
Vigna.

Signed-off-by: Richard Yao <richard.yao@alumni.stonybrook.edu>
  • Loading branch information
ryao committed Oct 2, 2022
1 parent 67395be commit c2e5ae4
Showing 1 changed file with 52 additions and 25 deletions.
77 changes: 52 additions & 25 deletions module/os/linux/spl/spl-generic.c
Expand Up @@ -60,10 +60,10 @@ proc_t p0;
EXPORT_SYMBOL(p0);

/*
* Xorshift Pseudo Random Number Generator based on work by Sebastiano Vigna
* xoshiro256++ 1.0 PRNG by David Blackman and Sebastiano Vigna
*
* "Further scramblings of Marsaglia's xorshift generators"
* http://vigna.di.unimi.it/ftp/papers/xorshiftplus.pdf
* "Scrambled Linear Pseudorandom Number Generators∗"
* https://vigna.di.unimi.it/ftp/papers/ScrambledLinear.pdf
*
* random_get_pseudo_bytes() is an API function on Illumos whose sole purpose
* is to provide bytes containing random numbers. It is mapped to /dev/urandom
Expand All @@ -75,14 +75,14 @@ EXPORT_SYMBOL(p0);
* free of atomic instructions.
*
* A consequence of using a fast PRNG is that using random_get_pseudo_bytes()
* to generate words larger than 128 bits will paradoxically be limited to
* `2^128 - 1` possibilities. This is because we have a sequence of `2^128 - 1`
* 128-bit words and selecting the first will implicitly select the second. If
* to generate words larger than 256 bits will paradoxically be limited to
* `2^256 - 1` possibilities. This is because we have a sequence of `2^256 - 1`
* 256-bit words and selecting the first will implicitly select the second. If
* a caller finds this behavior undesirable, random_get_bytes() should be used
* instead.
*
* XXX: Linux interrupt handlers that trigger within the critical section
* formed by `s[1] = xp[1];` and `xp[0] = s[0];` and call this function will
* formed by `s[3] = xp[3];` and `xp[0] = s[0];` and call this function will
* see the same numbers. Nothing in the code currently calls this in an
* interrupt handler, so this is considered to be okay. If that becomes a
* problem, we could create a set of per-cpu variables for interrupt handlers
Expand All @@ -92,56 +92,77 @@ EXPORT_SYMBOL(p0);
void __percpu *spl_pseudo_entropy;

/*
* spl_rand_next()/spl_rand_jump() are copied from the following CC-0 licensed
* file:
* rotl()/spl_rand_next()/spl_rand_jump() are copied from the following CC-0
* licensed file:
*
* http://xorshift.di.unimi.it/xorshift128plus.c
* https://prng.di.unimi.it/xoshiro256plusplus.c
*/

static inline uint64_t rotl(const uint64_t x, int k)
{
return ((x << k) | (x >> (64 - k)));
}

static inline uint64_t
spl_rand_next(uint64_t *s)
{
uint64_t s1 = s[0];
const uint64_t s0 = s[1];
s[0] = s0;
s1 ^= s1 << 23; // a
s[1] = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5); // b, c
return (s[1] + s0);
const uint64_t result = rotl(s[0] + s[3], 23) + s[0];

const uint64_t t = s[1] << 17;

s[2] ^= s[0];
s[3] ^= s[1];
s[1] ^= s[2];
s[0] ^= s[3];

s[2] ^= t;

s[3] = rotl(s[3], 45);

return (result);
}

static inline void
spl_rand_jump(uint64_t *s)
{
static const uint64_t JUMP[] =
{ 0x8a5cd789635d2dff, 0x121fd2155c472f96 };
static const uint64_t JUMP[] = { 0x180ec6d33cfd0aba,
0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c };

uint64_t s0 = 0;
uint64_t s1 = 0;
uint64_t s2 = 0;
uint64_t s3 = 0;
int i, b;
for (i = 0; i < sizeof (JUMP) / sizeof (*JUMP); i++)
for (b = 0; b < 64; b++) {
if (JUMP[i] & 1ULL << b) {
s0 ^= s[0];
s1 ^= s[1];
s2 ^= s[2];
s3 ^= s[3];
}
(void) spl_rand_next(s);
}

s[0] = s0;
s[1] = s1;
s[2] = s2;
s[3] = s3;
}

int
random_get_pseudo_bytes(uint8_t *ptr, size_t len)
{
uint64_t *xp, s[2];
uint64_t *xp, s[4];

ASSERT(ptr);

xp = get_cpu_ptr(spl_pseudo_entropy);

s[0] = xp[0];
s[1] = xp[1];
s[2] = xp[2];
s[3] = xp[3];

while (len) {
union {
Expand All @@ -159,6 +180,8 @@ random_get_pseudo_bytes(uint8_t *ptr, size_t len)

xp[0] = s[0];
xp[1] = s[1];
xp[2] = s[2];
xp[3] = s[3];

put_cpu_ptr(spl_pseudo_entropy);

Expand Down Expand Up @@ -741,28 +764,30 @@ spl_kvmem_init(void)
static int __init
spl_random_init(void)
{
uint64_t s[2];
uint64_t s[4];
int i = 0;

spl_pseudo_entropy = __alloc_percpu(2 * sizeof (uint64_t),
spl_pseudo_entropy = __alloc_percpu(4 * sizeof (uint64_t),
sizeof (uint64_t));

if (!spl_pseudo_entropy)
return (-ENOMEM);

get_random_bytes(s, sizeof (s));

if (s[0] == 0 && s[1] == 0) {
if (s[0] == 0 && s[1] == 0 && s[2] == 0 && s[3] == 0) {
if (jiffies != 0) {
s[0] = jiffies;
s[1] = ~0 - jiffies;
s[2] = ~jiffies;
s[3] = jiffies - ~0;
} else {
(void) memcpy(s, "improbable seed", sizeof (s));
(void) memcpy(s, "improbable seed", 16);
}
printk("SPL: get_random_bytes() returned 0 "
"when generating random seed. Setting initial seed to "
"0x%016llx%016llx.\n", cpu_to_be64(s[0]),
cpu_to_be64(s[1]));
"0x%016llx%016llx%016llx%016llx.\n", cpu_to_be64(s[0]),
cpu_to_be64(s[1]), cpu_to_be64(s[2]), cpu_to_be64(s[3]));
}

for_each_possible_cpu(i) {
Expand All @@ -772,6 +797,8 @@ spl_random_init(void)

wordp[0] = s[0];
wordp[1] = s[1];
wordp[2] = s[2];
wordp[3] = s[3];
}

return (0);
Expand Down

0 comments on commit c2e5ae4

Please sign in to comment.