std::rand: make the windows OSRng more correct, remove some C++.

This lets the C++ code in the rt handle the (slightly) tricky parts of
random number generation: e.g. error detection/handling, and using the
values of the `#define`d options to the various functions.
This commit is contained in:
Huon Wilson 2013-10-02 02:18:57 +10:00
parent fb9706338d
commit 29e3b33a09
8 changed files with 110 additions and 152 deletions

View File

@ -86,7 +86,6 @@ RUNTIME_CXXS_$(1)_$(2) := \
rt/sync/lock_and_signal.cpp \
rt/sync/rust_thread.cpp \
rt/rust_builtin.cpp \
rt/rust_rng.cpp \
rt/rust_upcall.cpp \
rt/rust_uv.cpp \
rt/miniz.cpp \

View File

@ -19,12 +19,8 @@ use rand::reader::ReaderRng;
#[cfg(unix)]
use rt::io::{file, Open, Read};
#[cfg(windows)]
use ptr;
#[cfg(windows)]
use cast;
#[cfg(windows)]
use libc::{GetLastError, FALSE};
/// A random number generator that retrieves randomness straight from
/// the operating system. On Unix-like systems this reads from
@ -40,9 +36,6 @@ pub struct OSRng {
/// `/dev/urandom`, on Windows this uses `CryptGenRandom`.
///
/// This does not block.
///
/// XXX: it is unlikely that this is threadsafe with the use of
/// GetLastError.
#[cfg(windows)]
pub struct OSRng {
priv hcryptprov: raw::HCRYPTPROV
@ -60,12 +53,10 @@ impl OSRng {
/// Create a new `OSRng`.
#[cfg(windows)]
#[fixed_stack_segment] #[inline(never)]
pub fn new() -> OSRng {
let hcp = ptr::mut_null();
// TODO these two 0 constants are incorrect!
if unsafe { raw::CryptAcquireContext(hcp, ptr::null(), ptr::null(), 0, 0); } == FALSE {
fail!("CryptAcquireContext failed with error %u", unsafe {GetLastError()})
}
let mut hcp = 0;
unsafe {raw::rust_win32_rand_acquire(&mut hcp)};
OSRng { hcryptprov: hcp }
}
@ -96,9 +87,12 @@ impl Rng for OSRng {
self.fill_bytes(v);
unsafe { cast::transmute(v) }
}
#[fixed_stack_segment] #[inline(never)]
fn fill_bytes(&mut self, v: &mut [u8]) {
if unsafe { raw::CryptGenRandom(self.hcryptprov, v.len(), v.unsafe_mut_ref(0)) } == FALSE {
fail!("CryptGenRandom failed with error %u", unsafe {GetLastError()})
use libc::DWORD;
do v.as_mut_buf |ptr, len| {
unsafe {raw::rust_win32_rand_gen(self.hcryptprov, len as DWORD, ptr)}
}
}
}
@ -111,27 +105,24 @@ impl Drop for OSRng {
}
#[cfg(windows)]
#[fixed_stack_segment] #[inline(never)]
fn drop(&mut self) {
// TODO this 0 means?
if unsafe { raw::CryptReleaseContext(self.hcryptprov, 0)} == FALSE {
fail!("CryptReleaseContext failed with error %u", unsafe {GetLastError()})
}
unsafe {raw::rust_win32_rand_release(self.hcryptprov)}
}
}
#[abi = "cdecl"]
#[cfg(windows)]
mod raw {
use libc::{LPCTSTR, DWORD, BOOL, BYTE};
use libc::{c_long, DWORD, BYTE};
enum HCRYPTPROV_opaque {}
pub type HCRYPTPROV = *CRYPTPROV;
pub type HCRYPTPROV = c_long;
// these functions are implemented so that they either succeed or
// abort(), so we can just assume they work when we call them.
extern {
pub fn CryptAcquireContext(phProv: *mut HCRYPTPROV,
pszContainer: LPCTSTR, pszProvider: LPCTSTR,
dwProvType: DWORD, dwFlags: DWORD) -> BOOL;
pub fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *mut BYTE) -> BOOL;
pub fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
pub fn rust_win32_rand_acquire(phProv: *mut HCRYPTPROV);
pub fn rust_win32_rand_gen(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *mut BYTE);
pub fn rust_win32_rand_release(hProv: HCRYPTPROV);
}
}

View File

@ -8,15 +8,28 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use option::{Some, None};
use rt::io::Reader;
use rt::io::ReaderByteConversions;
use rand::Rng;
/// An RNG that reads random bytes straight from a `Reader`. This will
/// work best with an infinite reader, but this is not required. The
/// semantics of reading past the end of the reader are the same as
/// those of the `read` method of the inner `Reader`.
/// work best with an infinite reader, but this is not required.
///
/// It will fail if it there is insufficient data to fulfill a request.
///
/// # Example
///
/// ```rust
/// use std::rand::reader;
/// use std::rt::io::mem;
///
/// fn main() {
/// let mut rng = reader::ReaderRng::new(mem::MemReader::new(~[1,2,3,4,5,6,7,8]));
/// println!("{}", rng.gen::<uint>());
/// }
/// ```
pub struct ReaderRng<R> {
priv reader: R
}
@ -32,8 +45,6 @@ impl<R: Reader> ReaderRng<R> {
impl<R: Reader> Rng for ReaderRng<R> {
fn next_u32(&mut self) -> u32 {
// XXX which is better: consistency between big/little-endian
// platforms, or speed.
if cfg!(target_endian="little") {
self.reader.read_le_u32_()
} else {
@ -48,8 +59,13 @@ impl<R: Reader> Rng for ReaderRng<R> {
}
}
fn fill_bytes(&mut self, v: &mut [u8]) {
// XXX: check that we filled `v``
let _n = self.reader.read(v);
if v.len() == 0 { return }
match self.reader.read(v) {
Some(n) if n == v.len() => return,
Some(n) => fail2!("ReaderRng.fill_bytes could not fill buffer: \
read {} out of {} bytes.", n, v.len()),
None => fail2!("ReaderRng.fill_bytes reached eof.")
}
}
}
@ -91,4 +107,12 @@ mod test {
assert_eq!(v, w);
}
#[test]
#[should_fail]
fn test_reader_rng_insufficient_bytes() {
let mut rng = ReaderRng::new(MemReader::new(~[]));
let mut v = [0u8, .. 3];
rng.fill_bytes(v);
}
}

View File

@ -26,7 +26,7 @@ use rt::local::Local;
use rt::rtio::{RemoteCallback, PausibleIdleCallback};
use borrow::{to_uint};
use cell::Cell;
use rand::{SeedableRng, XorShiftRng, Rng, Rand};
use rand::{XorShiftRng, Rng, Rand};
use iter::range;
use vec::{OwnedVector};
@ -862,6 +862,7 @@ fn new_sched_rng() -> XorShiftRng {
use ptr::RawPtr;
use vec::MutableVector;
use iter::Iterator;
use rand::SeedableRng;
// XXX: this could use io::native::file, when it works.
let file = do "/dev/urandom".with_c_str |name| {

View File

@ -15,7 +15,6 @@
#include "sync/lock_and_signal.h"
#include "memory_region.h"
#include "boxed_region.h"
#include "rust_rng.h"
#include "vg/valgrind.h"
#include "sp.h"
@ -69,11 +68,6 @@ rust_env_pairs() {
}
#endif
extern "C" CDECL void
rand_gen_seed(uint8_t* dest, size_t size) {
rng_gen_seed(dest, size);
}
extern "C" CDECL char*
#if defined(__WIN32__)
rust_list_dir_val(WIN32_FIND_DATA* entry_ptr) {
@ -654,6 +648,62 @@ rust_unset_sigprocmask() {
#endif
#if defined(__WIN32__)
void
win32_require(LPCTSTR fn, BOOL ok) {
if (!ok) {
LPTSTR buf;
DWORD err = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &buf, 0, NULL );
fprintf(stderr, "%s failed with error %ld: %s", fn, err, buf);
LocalFree((HLOCAL)buf);
abort();
}
}
extern "C" CDECL void
rust_win32_rand_acquire(HCRYPTPROV* phProv) {
win32_require
(_T("CryptAcquireContext"),
CryptAcquireContext(phProv, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
}
extern "C" CDECL void
rust_win32_rand_gen(HCRYPTPROV hProv, DWORD dwLen, BYTE* pbBuffer) {
win32_require
(_T("CryptGenRandom"), CryptGenRandom(hProv, dwLen, pbBuffer));
}
extern "C" CDECL void
rust_win32_rand_release(HCRYPTPROV hProv) {
win32_require
(_T("CryptReleaseContext"), CryptReleaseContext(hProv, 0));
}
#else
// these symbols are listed in rustrt.def.in, so they need to exist; but they
// should never be called.
extern "C" CDECL void
rust_win32_rand_acquire() {
abort();
}
extern "C" CDECL void
rust_win32_rand_gen() {
abort();
}
extern "C" CDECL void
rust_win32_rand_release() {
abort();
}
#endif
//
// Local Variables:
// mode: C++

View File

@ -1,83 +0,0 @@
// 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"
#ifdef __WIN32__
void
win32_require(LPCTSTR fn, BOOL ok) {
if (!ok) {
LPTSTR buf;
DWORD err = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &buf, 0, NULL );
fprintf(stderr, "%s failed with error %ld: %s", fn, err, buf);
LocalFree((HLOCAL)buf);
abort();
}
}
#endif
void
rng_gen_seed(uint8_t* dest, size_t size) {
#ifdef __WIN32__
HCRYPTPROV hProv;
win32_require
(_T("CryptAcquireContext"),
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
win32_require
(_T("CryptGenRandom"), CryptGenRandom(hProv, size, (BYTE*) dest));
win32_require
(_T("CryptReleaseContext"), CryptReleaseContext(hProv, 0));
#else
int fd = open("/dev/urandom", O_RDONLY);
if (fd == -1) {
fprintf(stderr, "error opening /dev/urandom: %s", strerror(errno));
abort();
}
size_t amount = 0;
do {
ssize_t ret = read(fd, dest+amount, size-amount);
if (ret < 0) {
fprintf(stderr, "error reading /dev/urandom: %s", strerror(errno));
abort();
}
else if (ret == 0) {
fprintf(stderr, "somehow hit eof reading from /dev/urandom");
abort();
}
amount += (size_t)ret;
} while (amount < size);
int ret = close(fd);
if (ret != 0) {
fprintf(stderr, "error closing /dev/urandom: %s", strerror(errno));
// FIXME #3697: Why does this fail sometimes?
// abort();
}
#endif
}
//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//

View File

@ -1,26 +0,0 @@
// 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
void rng_gen_seed(uint8_t* dest, size_t size);
//
// 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

@ -9,7 +9,6 @@ rust_localtime
rust_timegm
rust_mktime
precise_time_ns
rand_gen_seed
rust_path_is_dir
rust_path_exists
rust_get_stdin
@ -23,6 +22,9 @@ rust_log_console_off
rust_should_log_console
rust_unset_sigprocmask
rust_env_pairs
rust_win32_rand_acquire
rust_win32_rand_gen
rust_win32_rand_release
upcall_rust_personality
upcall_call_shim_on_c_stack
upcall_call_shim_on_rust_stack