auto merge of #4958 : cpeterso/rust/reseed-rng, r=graydon

For Issue #4709:

**c531506 rt: rand.rs expects `rust_next()` to return `uint32_t`, not `size_t`**

rand.rs expects `rustrt::rand_next()` to return `u32`, but the `rand_next()` C function returns `size_t`: ca71c6ec5b/src/libcore/rand.rs (L34)

**f4320b6 move isaac RNG utility functions to new rust_rng.cpp file**
**665e900 encapsulate isaac RNG in `rust_rng` struct**

Move isaac's `randctx` into a `rust_rng` struct to make names similar to `rand::Rng` function names and prepare for auto-reseeding in the next commit.

**9a78dc9 reseed `rust_rng` after generating 32KB**

Precedents from other languages:
* Haskell's `GenAutoReseed` generator reseeds itself after generating 32KB: http://hackage.haskell.org/packages/archive/DRBG/0.1.2/doc/html/Crypto-Random-DRBG.html#t:GenAutoReseed

* Go's RNG reseeds itself after generating 1MB: https://code.google.com/p/go/source/browse/src/pkg/crypto/rand/rand_unix.go?name=go1.0.3#94

**9a76d71 don't deplete RNG entropy when there is only one runnable task**

`rust_sched_loop::schedule_task()` unnecessarily calls `isaac_rand()` for the common case when there is only 1 runnable task, thus depleting RNG entropy and incurring unnecessary overhead.
This commit is contained in:
bors 2013-02-15 10:06:12 -08:00
commit 0a1fcadc90
9 changed files with 189 additions and 92 deletions

View File

@ -50,6 +50,7 @@ RUNTIME_CXXS_$(1) := \
rt/rust_builtin.cpp \
rt/rust_run_program.cpp \
rt/rust_env.cpp \
rt/rust_rng.cpp \
rt/rust_sched_loop.cpp \
rt/rust_sched_launcher.cpp \
rt/rust_sched_driver.cpp \

View File

@ -116,15 +116,15 @@ impl<T: Rand> Rand for Option<T> {
}
#[allow(non_camel_case_types)] // runtime type
enum rctx {}
enum rust_rng {}
#[abi = "cdecl"]
extern mod rustrt {
unsafe fn rand_seed() -> ~[u8];
unsafe fn rand_new() -> *rctx;
unsafe fn rand_new_seeded2(&&seed: ~[u8]) -> *rctx;
unsafe fn rand_next(c: *rctx) -> u32;
unsafe fn rand_free(c: *rctx);
unsafe fn rand_new() -> *rust_rng;
unsafe fn rand_new_seeded2(&&seed: ~[u8]) -> *rust_rng;
unsafe fn rand_next(rng: *rust_rng) -> u32;
unsafe fn rand_free(rng: *rust_rng);
}
/// A random number generator
@ -363,24 +363,24 @@ impl Rng {
}
struct RandRes {
c: *rctx,
rng: *rust_rng,
drop {
unsafe {
rustrt::rand_free(self.c);
rustrt::rand_free(self.rng);
}
}
}
fn RandRes(c: *rctx) -> RandRes {
fn RandRes(rng: *rust_rng) -> RandRes {
RandRes {
c: c
rng: rng
}
}
impl Rng for @RandRes {
fn next() -> u32 {
unsafe {
return rustrt::rand_next((*self).c);
return rustrt::rand_next((*self).rng);
}
}
}

View File

@ -135,7 +135,7 @@ rand_seed() {
rust_vec *v = (rust_vec *) task->kernel->malloc(vec_size<uint8_t>(size),
"rand_seed");
v->fill = v->alloc = size;
isaac_seed(task->kernel, (uint8_t*) &v->data, size);
rng_gen_seed(task->kernel, (uint8_t*) &v->data, size);
return v;
}
@ -143,27 +143,27 @@ extern "C" CDECL void *
rand_new() {
rust_task *task = rust_get_current_task();
rust_sched_loop *thread = task->sched_loop;
randctx *rctx = (randctx *) task->malloc(sizeof(randctx), "rand_new");
if (!rctx) {
rust_rng *rng = (rust_rng *) task->malloc(sizeof(rust_rng), "rand_new");
if (!rng) {
task->fail();
return NULL;
}
isaac_init(thread->kernel, rctx, NULL);
return rctx;
rng_init(thread->kernel, rng, NULL);
return rng;
}
extern "C" CDECL void *
rand_new_seeded(rust_vec_box* seed) {
rust_task *task = rust_get_current_task();
rust_sched_loop *thread = task->sched_loop;
randctx *rctx = (randctx *) task->malloc(sizeof(randctx),
"rand_new_seeded");
if (!rctx) {
rust_rng *rng = (rust_rng *) task->malloc(sizeof(rust_rng),
"rand_new_seeded");
if (!rng) {
task->fail();
return NULL;
}
isaac_init(thread->kernel, rctx, seed);
return rctx;
rng_init(thread->kernel, rng, seed);
return rng;
}
extern "C" CDECL void *
@ -171,15 +171,16 @@ rand_new_seeded2(rust_vec_box** seed) {
return rand_new_seeded(*seed);
}
extern "C" CDECL size_t
rand_next(randctx *rctx) {
return isaac_rand(rctx);
extern "C" CDECL uint32_t
rand_next(rust_rng *rng) {
rust_task *task = rust_get_current_task();
return rng_gen_u32(task->kernel, rng);
}
extern "C" CDECL void
rand_free(randctx *rctx) {
rand_free(rust_rng *rng) {
rust_task *task = rust_get_current_task();
task->free(rctx);
task->free(rng);
}

View File

@ -37,7 +37,6 @@
#include <math.h>
#include <assert.h>
#include "rand.h"
#include "uthash.h"
#if defined(__WIN32__)

115
src/rt/rust_rng.cpp Normal file
View File

@ -0,0 +1,115 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#include "rust_globals.h"
#include "rust_rng.h"
#include "rust_util.h"
// Initialization helpers for ISAAC RNG
void
rng_gen_seed(rust_kernel* kernel, uint8_t* dest, size_t size) {
#ifdef __WIN32__
HCRYPTPROV hProv;
kernel->win32_require
(_T("CryptAcquireContext"),
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
kernel->win32_require
(_T("CryptGenRandom"), CryptGenRandom(hProv, size, (BYTE*) dest));
kernel->win32_require
(_T("CryptReleaseContext"), CryptReleaseContext(hProv, 0));
#else
int fd = open("/dev/urandom", O_RDONLY);
if (fd == -1)
kernel->fatal("error opening /dev/urandom: %s", strerror(errno));
size_t amount = 0;
do {
ssize_t ret = read(fd, dest+amount, size-amount);
if (ret < 0)
kernel->fatal("error reading /dev/urandom: %s", strerror(errno));
else if (ret == 0)
kernel->fatal("somehow hit eof reading from /dev/urandom");
amount += (size_t)ret;
} while (amount < size);
int ret = close(fd);
// FIXME #3697: Why does this fail sometimes?
if (ret != 0)
kernel->log(log_warn, "error closing /dev/urandom: %s",
strerror(errno));
#endif
}
static void
isaac_init(rust_kernel *kernel, randctx *rctx, rust_vec_box* user_seed) {
memset(rctx, 0, sizeof(randctx));
char *env_seed = kernel->env->rust_seed;
if (user_seed != NULL) {
// ignore bytes after the required length
size_t seed_len = user_seed->body.fill < sizeof(rctx->randrsl)
? user_seed->body.fill : sizeof(rctx->randrsl);
memcpy(&rctx->randrsl, user_seed->body.data, seed_len);
} else if (env_seed != NULL) {
ub4 seed = (ub4) atoi(env_seed);
for (size_t i = 0; i < RANDSIZ; i ++) {
memcpy(&rctx->randrsl[i], &seed, sizeof(ub4));
seed = (seed + 0x7ed55d16) + (seed << 12);
}
} else {
rng_gen_seed(kernel, (uint8_t*)&rctx->randrsl, sizeof(rctx->randrsl));
}
randinit(rctx, 1);
}
void
rng_init(rust_kernel* kernel, rust_rng* rng, rust_vec_box* user_seed) {
isaac_init(kernel, &rng->rctx, user_seed);
rng->reseedable = !user_seed && !kernel->env->rust_seed;
}
static void
rng_maybe_reseed(rust_kernel* kernel, rust_rng* rng) {
// If this RNG has generated more than 32KB of random data and was not
// seeded by the user or RUST_SEED, then we should reseed now.
const size_t RESEED_THRESHOLD = 32 * 1024;
size_t bytes_generated = rng->rctx.randc * sizeof(ub4);
if (bytes_generated < RESEED_THRESHOLD || !rng->reseedable) {
return;
}
uint32_t new_seed[RANDSIZ];
rng_gen_seed(kernel, (uint8_t*) new_seed, RANDSIZ * sizeof(uint32_t));
// Stir new seed into PRNG's entropy pool.
for (size_t i = 0; i < RANDSIZ; i++) {
rng->rctx.randrsl[i] ^= new_seed[i];
}
randinit(&rng->rctx, 1);
}
uint32_t
rng_gen_u32(rust_kernel* kernel, rust_rng* rng) {
uint32_t x = isaac_rand(&rng->rctx);
rng_maybe_reseed(kernel, rng);
return x;
}
//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//

40
src/rt/rust_rng.h Normal file
View File

@ -0,0 +1,40 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#ifndef RUST_RNG_H
#define RUST_RNG_H
#include "rand.h"
class rust_kernel;
struct rust_vec_box;
// Initialization helpers for ISAAC RNG
struct rust_rng {
randctx rctx;
bool reseedable;
};
void rng_gen_seed(rust_kernel* kernel, uint8_t* dest, size_t size);
void rng_init(rust_kernel *kernel, rust_rng *rng, rust_vec_box* user_seed);
uint32_t rng_gen_u32(rust_kernel *kernel, rust_rng *rng);
//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
#endif

View File

@ -41,7 +41,7 @@ rust_sched_loop::rust_sched_loop(rust_scheduler *sched, int id, bool killed) :
name("main")
{
LOGPTR(this, "new dom", (uintptr_t)this);
isaac_init(kernel, &rctx, NULL);
rng_init(kernel, &rng, NULL);
if (!tls_initialized)
init_tls();
@ -150,10 +150,10 @@ rust_sched_loop::release_task(rust_task *task) {
rust_task *
rust_sched_loop::schedule_task() {
lock.must_have_lock();
if (running_tasks.length() > 0) {
size_t k = isaac_rand(&rctx);
size_t i = k % running_tasks.length();
return (rust_task *)running_tasks[i];
size_t tasks = running_tasks.length();
if (tasks > 0) {
size_t i = (tasks > 1) ? (rng_gen_u32(kernel, &rng) % tasks) : 0;
return running_tasks[i];
}
return NULL;
}

View File

@ -13,6 +13,7 @@
#include "rust_globals.h"
#include "rust_log.h"
#include "rust_rng.h"
#include "rust_stack.h"
#include "rust_signal.h"
#include "context.h"
@ -61,7 +62,7 @@ private:
#endif
context c_context;
rust_rng rng;
bool should_exit;
stk_seg *cached_c_stack;
@ -102,7 +103,6 @@ public:
size_t min_stack_size;
memory_region local_region;
randctx rctx;
const char *const name; // Used for debugging
// Only a pointer to 'name' is kept, so it must live as long as this

View File

@ -136,65 +136,6 @@ inline size_t get_box_size(size_t body_size, size_t body_align) {
return total_size;
}
// Initialization helpers for ISAAC RNG
inline void isaac_seed(rust_kernel* kernel, uint8_t* dest, size_t size)
{
#ifdef __WIN32__
HCRYPTPROV hProv;
kernel->win32_require
(_T("CryptAcquireContext"),
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
kernel->win32_require
(_T("CryptGenRandom"), CryptGenRandom(hProv, size, (BYTE*) dest));
kernel->win32_require
(_T("CryptReleaseContext"), CryptReleaseContext(hProv, 0));
#else
int fd = open("/dev/urandom", O_RDONLY);
if (fd == -1)
kernel->fatal("error opening /dev/urandom: %s", strerror(errno));
size_t amount = 0;
do {
ssize_t ret = read(fd, dest+amount, size-amount);
if (ret < 0)
kernel->fatal("error reading /dev/urandom: %s", strerror(errno));
else if (ret == 0)
kernel->fatal("somehow hit eof reading from /dev/urandom");
amount += (size_t)ret;
} while (amount < size);
int ret = close(fd);
// FIXME #3697: Why does this fail sometimes?
if (ret != 0)
kernel->log(log_warn, "error closing /dev/urandom: %s",
strerror(errno));
#endif
}
inline void
isaac_init(rust_kernel *kernel, randctx *rctx, rust_vec_box* user_seed)
{
memset(rctx, 0, sizeof(randctx));
char *env_seed = kernel->env->rust_seed;
if (user_seed != NULL) {
// ignore bytes after the required length
size_t seed_len = user_seed->body.fill < sizeof(rctx->randrsl)
? user_seed->body.fill : sizeof(rctx->randrsl);
memcpy(&rctx->randrsl, user_seed->body.data, seed_len);
} else if (env_seed != NULL) {
ub4 seed = (ub4) atoi(env_seed);
for (size_t i = 0; i < RANDSIZ; i ++) {
memcpy(&rctx->randrsl[i], &seed, sizeof(ub4));
seed = (seed + 0x7ed55d16) + (seed << 12);
}
} else {
isaac_seed(kernel, (uint8_t*) &rctx->randrsl, sizeof(rctx->randrsl));
}
randinit(rctx, 1);
}
//
// Local Variables:
// mode: C++