Further reduce diffs with OpenBSD's arc4random. The main functional

change here is to ensure that when a process forks after arc4random
is seeded, the parent and child don't observe the same random sequence.
OpenBSD's fix introduces some additional overhead in the form of a
getpid() call.  This could be improved upon, e.g., by setting a flag
in fork(), if it proves to be a problem.

This was discussed with secteam (simon, csjp, rwatson) in 2008, shortly
prior to my going out of town and forgetting all about it.  The conclusion
was that the problem with forks is worrisome, but it doesn't appear to
have introduced an actual vulnerability for any known programs.

The only significant remaining difference between our arc4random and
OpenBSD's is in how we seed the generator in arc4_stir().
This commit is contained in:
das 2011-11-15 05:49:24 +00:00
parent 811ac31232
commit 81be797065

View File

@ -33,11 +33,13 @@
__FBSDID("$FreeBSD$");
#include "namespace.h"
#include <sys/types.h>
#include <sys/time.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <pthread.h>
#include "libc_private.h"
@ -71,9 +73,9 @@ static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;
_pthread_mutex_unlock(&arc4random_mtx); \
} while (0)
static struct arc4_stream rs;
static int rs_initialized;
static int rs_stired;
static struct arc4_stream rs;
static pid_t arc4_stir_pid;
static int arc4_count;
static inline u_int8_t arc4_getbyte(void);
@ -117,6 +119,10 @@ arc4_stir(void)
u_char rnd[KEYSIZE];
} rdat;
if (!rs_initialized) {
arc4_init();
rs_initialized = 1;
}
fd = _open(RANDOMDEV, O_RDONLY, 0);
done = 0;
if (fd >= 0) {
@ -141,6 +147,18 @@ arc4_stir(void)
arc4_count = 1600000;
}
static void
arc4_stir_if_needed(void)
{
pid_t pid = getpid();
if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid)
{
arc4_stir_pid = pid;
arc4_stir();
}
}
static inline u_int8_t
arc4_getbyte(void)
{
@ -166,31 +184,11 @@ arc4_getword(void)
return val;
}
static void
arc4_check_init(void)
{
if (!rs_initialized) {
arc4_init();
rs_initialized = 1;
}
}
static inline void
arc4_check_stir(void)
{
if (!rs_stired || arc4_count <= 0) {
arc4_stir();
rs_stired = 1;
}
}
void
arc4random_stir(void)
{
_ARC4_LOCK();
arc4_check_init();
arc4_stir();
rs_stired = 1;
_ARC4_UNLOCK();
}
@ -198,8 +196,8 @@ void
arc4random_addrandom(u_char *dat, int datlen)
{
_ARC4_LOCK();
arc4_check_init();
arc4_check_stir();
if (!rs_initialized)
arc4_stir();
arc4_addrandom(dat, datlen);
_ARC4_UNLOCK();
}
@ -209,10 +207,9 @@ arc4random(void)
{
u_int32_t val;
_ARC4_LOCK();
arc4_check_init();
arc4_check_stir();
val = arc4_getword();
arc4_count -= 4;
arc4_stir_if_needed();
val = arc4_getword();
_ARC4_UNLOCK();
return val;
}
@ -222,11 +219,11 @@ arc4random_buf(void *_buf, size_t n)
{
u_char *buf = (u_char *)_buf;
_ARC4_LOCK();
arc4_check_init();
arc4_stir_if_needed();
while (n--) {
arc4_check_stir();
if (--arc4_count <= 0)
arc4_stir();
buf[n] = arc4_getbyte();
arc4_count--;
}
_ARC4_UNLOCK();
}