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:
commit
0a1fcadc90
1
mk/rt.mk
1
mk/rt.mk
@ -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 \
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
115
src/rt/rust_rng.cpp
Normal 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
40
src/rt/rust_rng.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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++
|
||||
|
Loading…
Reference in New Issue
Block a user