I accidentally broke this code during an un-optimization. We need to
start from a random value of the width of the limit, that is, 32 bits.
Thanks to Jason for pointing to his similar code in the kernel, which
made me see my mistake.
Fixes: 2a61122b5e ("Unoptimize the higher part of the domain of csrand_uniform()")
Closes: <https://github.com/shadow-maint/shadow/issues/1015>
Reported-by: Michael Brunnbauer <https://github.com/michaelbrunnbauer>
Link: <https://git.zx2c4.com/linux-rng/tree/drivers/char/random.c#n535>
Cc: "Jason A. Donenfeld" <Jason@zx2c4.com>
Link: <https://github.com/shadow-maint/shadow/pull/638>
Link: <https://github.com/shadow-maint/shadow/issues/634>
Link: <https://github.com/shadow-maint/shadow/pull/624>
Tested-by: Michael Brunnbauer <https://github.com/michaelbrunnbauer>
Reviewed-by: Michael Brunnbauer <https://github.com/michaelbrunnbauer>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
151 lines
2.6 KiB
C
151 lines
2.6 KiB
C
/*
|
|
* SPDX-FileCopyrightText: Alejandro Colomar <alx@kernel.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#ident "$Id$"
|
|
|
|
#include <limits.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#if HAVE_SYS_RANDOM_H
|
|
#include <sys/random.h>
|
|
#endif
|
|
#include "bit.h"
|
|
#include "defines.h"
|
|
#include "prototypes.h"
|
|
#include "shadowlog.h"
|
|
#include "sizeof.h"
|
|
|
|
|
|
static uint32_t csrand32(void);
|
|
static uint32_t csrand_uniform32(uint32_t n);
|
|
static unsigned long csrand_uniform_slow(unsigned long n);
|
|
|
|
|
|
/*
|
|
* Return a uniformly-distributed CS random u_long value.
|
|
*/
|
|
unsigned long
|
|
csrand(void)
|
|
{
|
|
FILE *fp;
|
|
unsigned long r;
|
|
|
|
#ifdef HAVE_GETENTROPY
|
|
/* getentropy may exist but lack kernel support. */
|
|
if (getentropy(&r, sizeof(r)) == 0)
|
|
return r;
|
|
#endif
|
|
|
|
#ifdef HAVE_GETRANDOM
|
|
/* Likewise getrandom. */
|
|
if (getrandom(&r, sizeof(r), 0) == sizeof(r))
|
|
return r;
|
|
#endif
|
|
|
|
#ifdef HAVE_ARC4RANDOM_BUF
|
|
/* arc4random_buf can never fail. */
|
|
arc4random_buf(&r, sizeof(r));
|
|
return r;
|
|
#endif
|
|
|
|
/* Use /dev/urandom as a last resort. */
|
|
fp = fopen("/dev/urandom", "r");
|
|
if (NULL == fp) {
|
|
goto fail;
|
|
}
|
|
|
|
if (fread(&r, sizeof(r), 1, fp) != 1) {
|
|
fclose(fp);
|
|
goto fail;
|
|
}
|
|
|
|
fclose(fp);
|
|
return r;
|
|
|
|
fail:
|
|
fprintf(log_get_logfd(), _("Unable to obtain random bytes.\n"));
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return a uniformly-distributed CS random value in the interval [0, n-1].
|
|
*/
|
|
unsigned long
|
|
csrand_uniform(unsigned long n)
|
|
{
|
|
if (n == 0 || n > UINT32_MAX)
|
|
return csrand_uniform_slow(n);
|
|
|
|
return csrand_uniform32(n);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return a uniformly-distributed CS random value in the interval [min, max].
|
|
*/
|
|
unsigned long
|
|
csrand_interval(unsigned long min, unsigned long max)
|
|
{
|
|
return csrand_uniform(max - min + 1) + min;
|
|
}
|
|
|
|
|
|
static uint32_t
|
|
csrand32(void)
|
|
{
|
|
return csrand();
|
|
}
|
|
|
|
|
|
/*
|
|
* Fast Random Integer Generation in an Interval
|
|
* ACM Transactions on Modeling and Computer Simulation 29 (1), 2019
|
|
* <https://arxiv.org/abs/1805.10941>
|
|
*/
|
|
static uint32_t
|
|
csrand_uniform32(uint32_t n)
|
|
{
|
|
uint32_t bound, rem;
|
|
uint64_t r, mult;
|
|
|
|
if (n == 0)
|
|
return csrand32();
|
|
|
|
bound = -n % n; // analogous to `2^32 % n`, since `x % y == (x-y) % y`
|
|
|
|
do {
|
|
r = csrand32();
|
|
mult = r * n;
|
|
rem = mult; // analogous to `mult % 2^32`
|
|
} while (rem < bound); // p = (2^32 % n) / 2^32; W.C.: n=2^31+1, p=0.5
|
|
|
|
r = mult >> WIDTHOF(n); // analogous to `mult / 2^32`
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
static unsigned long
|
|
csrand_uniform_slow(unsigned long n)
|
|
{
|
|
unsigned long r, max, mask;
|
|
|
|
max = n - 1;
|
|
mask = bit_ceil_wrapul(n) - 1;
|
|
|
|
do {
|
|
r = csrand();
|
|
r &= mask; // optimization
|
|
} while (r > max); // p = ((mask+1) % n) / (mask+1); W.C.: p=0.5
|
|
|
|
return r;
|
|
}
|