virrandom: Make virRandomBits better

Now that we have strong PRNG generator implemented in
virRandomBytes() let's use that instead of gnulib's random_r.

Problem with the latter is in way we seed it: current UNIX time
and libvirtd's PID are not that random as one might think.
Imagine two hosts booting at the same time. There's a fair chance
that those hosts spawn libvirtds at the same time and with the
same PID. This will result in both daemons generating the same
sequence of say MAC addresses [1].

1: https://www.redhat.com/archives/libvirt-users/2018-May/msg00097.html

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Michal Privoznik 2018-05-29 08:26:18 +02:00
parent b87dda7549
commit 60da4a1148
2 changed files with 2 additions and 62 deletions

View File

@ -90,7 +90,6 @@ pthread
pthread_sigmask
recv
regex
random_r
sched
secure_getenv
send

View File

@ -49,53 +49,6 @@ VIR_LOG_INIT("util.random");
#define RANDOM_SOURCE "/dev/urandom"
/* The algorithm of virRandomBits relies on gnulib's guarantee that
* 'random_r' matches the POSIX requirements on 'random' of being
* evenly distributed among exactly [0, 2**31) (that is, we always get
* exactly 31 bits). While this happens to be the value of RAND_MAX
* on glibc, note that POSIX only requires RAND_MAX to be tied to the
* weaker 'rand', so there are platforms where RAND_MAX is smaller
* than the range of 'random_r'. For the results to be evenly
* distributed among up to 64 bits, we also rely on the period of
* 'random_r' to be at least 2**64, which POSIX only guarantees for
* 'random' if you use 256 bytes of state. */
enum {
RANDOM_BITS_PER_ITER = 31,
RANDOM_BITS_MASK = (1U << RANDOM_BITS_PER_ITER) - 1,
RANDOM_STATE_SIZE = 256,
};
static char randomState[RANDOM_STATE_SIZE];
static struct random_data randomData;
static virMutex randomLock = VIR_MUTEX_INITIALIZER;
static int
virRandomOnceInit(void)
{
unsigned int seed = time(NULL) ^ getpid();
#if 0
/* Normally we want a decent seed. But if reproducible debugging
* of a fixed pseudo-random sequence is ever required, uncomment
* this block to let an environment variable force the seed. */
const char *debug = virGetEnvBlockSUID("VIR_DEBUG_RANDOM_SEED");
if (debug && virStrToLong_ui(debug, NULL, 0, &seed) < 0)
return -1;
#endif
if (initstate_r(seed,
randomState,
sizeof(randomState),
&randomData) < 0)
return -1;
return 0;
}
VIR_ONCE_GLOBAL_INIT(virRandom)
/**
* virRandomBits:
* @nbits: Number of bits of randommess required
@ -108,26 +61,14 @@ VIR_ONCE_GLOBAL_INIT(virRandom)
uint64_t virRandomBits(int nbits)
{
uint64_t ret = 0;
int32_t bits;
if (virRandomInitialize() < 0) {
if (virRandomBytes((unsigned char *) &ret, sizeof(ret)) < 0) {
/* You're already hosed, so this particular non-random value
* isn't any worse. */
return 0;
}
virMutexLock(&randomLock);
while (nbits > RANDOM_BITS_PER_ITER) {
random_r(&randomData, &bits);
ret = (ret << RANDOM_BITS_PER_ITER) | (bits & RANDOM_BITS_MASK);
nbits -= RANDOM_BITS_PER_ITER;
}
random_r(&randomData, &bits);
ret = (ret << nbits) | (bits & ((1 << nbits) - 1));
virMutexUnlock(&randomLock);
ret &= (1U << nbits) - 1;
return ret;
}