Skip to content

Commit 927ab7b

Browse files
authored
Merge pull request #239 from dhardy/os-file-handle
Limit OsRng to a single file handle when reading from a file
2 parents 40a9c5c + 92cde65 commit 927ab7b

File tree

2 files changed

+50
-21
lines changed

2 files changed

+50
-21
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ sudo: false
33

44
matrix:
55
include:
6-
- rust: 1.15.0
6+
- rust: 1.22.0
77
script:
88
- cargo test
99
- cargo build --no-default-features # we cannot exclude doc tests

src/os.rs

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
1414
use std::fmt;
1515
use std::io::Read;
16+
#[allow(unused)] use std::fs::File;
17+
#[allow(unused)] use std::path::Path;
18+
#[allow(unused)] use std::sync::{Once, Mutex, ONCE_INIT};
1619

1720
use {Rng, Error, ErrorKind, impls};
1821

@@ -101,15 +104,53 @@ impl Rng for OsRng {
101104
}
102105

103106
// Specialisation of `ReadRng` for our purposes
107+
//
108+
// Note: all instances use a single internal file handle
104109
#[derive(Debug)]
105-
struct ReadRng<R> (R);
110+
#[allow(unused)] // not used by all targets
111+
struct ReadRng {}
112+
113+
// TODO: remove outer Option when `Mutex::new(None)` is a constant expression
114+
static mut READ_RNG_FILE: Option<Mutex<Option<File>>> = None;
115+
static READ_RNG_ONCE: Once = ONCE_INIT;
106116

107-
impl<R: Read> ReadRng<R> {
108-
#[allow(unused)] // not used by all targets
117+
#[allow(unused)] // not used by all targets
118+
impl ReadRng {
119+
// Open a `File` for the given `path`.
120+
//
121+
// Uses a mutex on a static object to limit `OsRng` to a single file descriptor.
122+
fn open<P: AsRef<Path>>(path: P) -> Result<ReadRng, Error> {
123+
READ_RNG_ONCE.call_once(|| {
124+
unsafe { READ_RNG_FILE = Some(Mutex::new(None)) }
125+
});
126+
127+
// We try opening the file outside the `call_once` fn because we cannot
128+
// clone the error, thus we must retry on failure.
129+
130+
let mutex = unsafe{ READ_RNG_FILE.as_ref().unwrap() };
131+
let mut guard = mutex.lock().unwrap();
132+
if (*guard).is_none() {
133+
let file = File::open(path).map_err(|err| Error::with_cause(
134+
ErrorKind::Unavailable,
135+
"error opening random device",
136+
err
137+
))?;
138+
*guard = Some(file);
139+
}
140+
141+
Ok(ReadRng {})
142+
}
143+
109144
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
110145
if dest.len() == 0 { return Ok(()); }
146+
147+
// Since we have an instance of Self, we can assume that our memory was
148+
// set with a valid object.
149+
let mutex = unsafe{ READ_RNG_FILE.as_ref().unwrap() };
150+
let mut guard = mutex.lock().unwrap();
151+
let mut file = (*guard).as_mut().unwrap();
111152
// Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`.
112-
self.0.read_exact(dest).map_err(|err| {
153+
file.read_exact(dest).map_err(|err| {
113154
Error::with_cause(ErrorKind::Unavailable, "error reading random device", err)
114155
})
115156
}
@@ -131,7 +172,6 @@ mod imp {
131172
use {Error, ErrorKind};
132173

133174
use std::io;
134-
use std::fs::File;
135175

136176
#[cfg(all(target_os = "linux",
137177
any(target_arch = "x86_64",
@@ -243,7 +283,7 @@ mod imp {
243283
#[derive(Debug)]
244284
enum OsRngInner {
245285
OsGetrandomRng,
246-
OsReadRng(ReadRng<File>),
286+
OsReadRng(ReadRng),
247287
}
248288

249289
impl OsRng {
@@ -252,11 +292,7 @@ mod imp {
252292
return Ok(OsRng { inner: OsGetrandomRng });
253293
}
254294

255-
let reader = File::open("/dev/urandom").map_err(|err| {
256-
Error::with_cause(ErrorKind::Unavailable, "error opening random device", err)
257-
})?;
258-
let reader_rng = ReadRng(reader);
259-
295+
let reader_rng = ReadRng::open("/dev/urandom")?;
260296
Ok(OsRng { inner: OsReadRng(reader_rng) })
261297
}
262298

@@ -411,23 +447,16 @@ mod imp {
411447
#[cfg(target_os = "redox")]
412448
mod imp {
413449
use {Error, ErrorKind};
414-
415-
use std::io;
416-
use std::fs::File;
417450
use super::ReadRng;
418451

419452
#[derive(Debug)]
420453
pub struct OsRng {
421-
inner: ReadRng<File>,
454+
inner: ReadRng,
422455
}
423456

424457
impl OsRng {
425458
pub fn new() -> Result<OsRng, Error> {
426-
let reader = File::open("rand:").map_err(|err| {
427-
Error::with_cause(ErrorKind::Unavailable, "error opening random device", err)
428-
})?;
429-
let reader_rng = ReadRng(reader);
430-
459+
let reader_rng = ReadRng::open("rand:")?;
431460
Ok(OsRng { inner: reader_rng })
432461
}
433462
pub fn try_fill_bytes(&mut self, v: &mut [u8]) -> Result<(), Error> {

0 commit comments

Comments
 (0)