Skip to content

Commit 70f7fad

Browse files
committed
Remove TLS by using lazy_static
1 parent ce4a089 commit 70f7fad

File tree

8 files changed

+118
-198
lines changed

8 files changed

+118
-198
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ log = { version = "0.4", optional = true }
2222

2323
[target.'cfg(unix)'.dependencies]
2424
libc = "0.2.29"
25+
lazy_static = "1.3.0"
2526

2627
[target.'cfg(windows)'.dependencies]
2728
winapi = { version = "0.3.6", features = ["minwindef", "ntsecapi", "winnt"] }
@@ -32,9 +33,13 @@ cloudabi = "0.0.3"
3233
[target.'cfg(fuchsia)'.dependencies]
3334
fuchsia-cprng = "0.1"
3435

36+
[target.'cfg(target_os = "redox")'.dependencies]
37+
lazy_static = "1.3.0"
38+
3539
[target.wasm32-unknown-unknown.dependencies]
3640
wasm-bindgen = { version = "0.2.29", optional = true }
3741
stdweb = { version = "0.4.9", optional = true }
42+
lazy_static = "1.3.0"
3843

3944
[target.wasm32-wasi.dependencies]
4045
libc = "0.2.54"

src/lib.rs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -132,20 +132,8 @@ macro_rules! error { ($($x:tt)*) => () }
132132
#[cfg(target_arch = "wasm32")]
133133
extern crate std;
134134

135-
#[cfg(any(
136-
target_os = "android",
137-
target_os = "netbsd",
138-
target_os = "solaris",
139-
target_os = "illumos",
140-
target_os = "redox",
141-
target_os = "dragonfly",
142-
target_os = "haiku",
143-
target_os = "linux",
144-
all(
145-
target_arch = "wasm32",
146-
not(target_os = "wasi")
147-
),
148-
))]
135+
#[cfg(any(unix, target_os = "redox"))]
136+
#[allow(unused)]
149137
mod utils;
150138
mod error;
151139
pub use crate::error::Error;

src/linux_android.rs

Lines changed: 19 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,30 @@
99
//! Implementation for Linux / Android
1010
extern crate std;
1111

12-
use crate::Error;
13-
use crate::utils::use_init;
14-
use std::{thread_local, io::{self, Read}, fs::File};
15-
use core::cell::RefCell;
12+
use crate::{utils::use_file, Error};
1613
use core::num::NonZeroU32;
17-
use core::sync::atomic::{AtomicBool, Ordering};
14+
use std::io;
15+
use lazy_static::lazy_static;
1816

1917
// This flag tells getrandom() to return EAGAIN instead of blocking.
2018
const GRND_NONBLOCK: libc::c_uint = 0x0001;
21-
static RNG_INIT: AtomicBool = AtomicBool::new(false);
2219

2320
enum RngSource {
2421
GetRandom,
25-
Device(File),
22+
File,
2623
}
2724

28-
thread_local!(
29-
static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
30-
);
25+
lazy_static! {
26+
static ref RNG_SOURCE: RngSource = {
27+
let mut buf: [u8; 0] = [];
28+
if let Err(err) = syscall_getrandom(&mut buf, false) {
29+
if err.raw_os_error() == Some(libc::ENOSYS) {
30+
return RngSource::File;
31+
}
32+
}
33+
RngSource::GetRandom
34+
};
35+
}
3136

3237
fn syscall_getrandom(dest: &mut [u8], block: bool) -> Result<(), io::Error> {
3338
let flags = if block { 0 } else { GRND_NONBLOCK };
@@ -42,46 +47,10 @@ fn syscall_getrandom(dest: &mut [u8], block: bool) -> Result<(), io::Error> {
4247
}
4348

4449
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
45-
RNG_SOURCE.with(|f| {
46-
use_init(f,
47-
|| {
48-
let s = if is_getrandom_available() {
49-
RngSource::GetRandom
50-
} else {
51-
// read one byte from "/dev/random" to ensure that
52-
// OS RNG has initialized
53-
if !RNG_INIT.load(Ordering::Relaxed) {
54-
File::open("/dev/random")?.read_exact(&mut [0u8; 1])?;
55-
RNG_INIT.store(true, Ordering::Relaxed)
56-
}
57-
RngSource::Device(File::open("/dev/urandom")?)
58-
};
59-
Ok(s)
60-
}, |f| {
61-
match f {
62-
RngSource::GetRandom => syscall_getrandom(dest, true),
63-
RngSource::Device(f) => f.read_exact(dest),
64-
}.map_err(From::from)
65-
})
66-
})
67-
}
68-
69-
fn is_getrandom_available() -> bool {
70-
use std::sync::{Once, ONCE_INIT};
71-
72-
static CHECKER: Once = ONCE_INIT;
73-
static AVAILABLE: AtomicBool = AtomicBool::new(false);
74-
75-
CHECKER.call_once(|| {
76-
let mut buf: [u8; 0] = [];
77-
let available = match syscall_getrandom(&mut buf, false) {
78-
Ok(()) => true,
79-
Err(err) => err.raw_os_error() != Some(libc::ENOSYS),
80-
};
81-
AVAILABLE.store(available, Ordering::Relaxed);
82-
});
83-
84-
AVAILABLE.load(Ordering::Relaxed)
50+
match *RNG_SOURCE {
51+
RngSource::GetRandom => syscall_getrandom(dest, true).map_err(From::from),
52+
RngSource::File => use_file(None, dest),
53+
}
8554
}
8655

8756
#[inline(always)]

src/solaris_illumos.rs

Lines changed: 25 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
//! libc::dlsym.
2020
extern crate std;
2121

22-
use crate::Error;
23-
use crate::utils::use_init;
24-
use std::{thread_local, io::{self, Read}, fs::File};
25-
use core::cell::RefCell;
22+
use crate::{utils::use_file, Error};
23+
use core::mem;
2624
use core::num::NonZeroU32;
25+
use std::io;
26+
use lazy_static::lazy_static;
2727

2828
#[cfg(target_os = "illumos")]
2929
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
@@ -32,12 +32,21 @@ type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) ->
3232

3333
enum RngSource {
3434
GetRandom(GetRandomFn),
35-
Device(File),
35+
File,
3636
}
3737

38-
thread_local!(
39-
static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
40-
);
38+
lazy_static! {
39+
static ref RNG_SOURCE: RngSource = {
40+
let name = "getrandom\0";
41+
let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) };
42+
43+
if addr.is_null() {
44+
RngSource::File
45+
} else {
46+
RngSource::GetRandom(unsafe { mem::transmute(addr) })
47+
}
48+
};
49+
}
4150

4251
fn libc_getrandom(rand: GetRandomFn, dest: &mut [u8]) -> Result<(), Error> {
4352
let ret = unsafe { rand(dest.as_mut_ptr(), dest.len(), 0) as libc::ssize_t };
@@ -53,49 +62,15 @@ fn libc_getrandom(rand: GetRandomFn, dest: &mut [u8]) -> Result<(), Error> {
5362
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
5463
// 256 bytes is the lowest common denominator across all the Solaris
5564
// derived platforms for atomically obtaining random data.
56-
RNG_SOURCE.with(|f| {
57-
use_init(
58-
f,
59-
|| {
60-
let s = match fetch_getrandom() {
61-
Some(fptr) => RngSource::GetRandom(fptr),
62-
None => RngSource::Device(File::open("/dev/random")?),
63-
};
64-
Ok(s)
65-
},
66-
|f| {
67-
match f {
68-
RngSource::GetRandom(rp) => {
69-
for chunk in dest.chunks_mut(256) {
70-
libc_getrandom(*rp, chunk)?
71-
}
72-
}
73-
RngSource::Device(randf) => {
74-
for chunk in dest.chunks_mut(256) {
75-
randf.read_exact(chunk)?
76-
}
77-
}
78-
};
79-
Ok(())
80-
},
81-
)
82-
})
83-
}
84-
85-
fn fetch_getrandom() -> Option<GetRandomFn> {
86-
use std::mem;
87-
use std::sync::atomic::{AtomicUsize, Ordering};
88-
89-
static FPTR: AtomicUsize = AtomicUsize::new(1);
90-
91-
if FPTR.load(Ordering::SeqCst) == 1 {
92-
let name = "getrandom\0";
93-
let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) as usize };
94-
FPTR.store(addr, Ordering::SeqCst);
65+
match *RNG_SOURCE {
66+
RngSource::GetRandom(rp) => {
67+
for chunk in dest.chunks_mut(256) {
68+
libc_getrandom(rp, chunk)?
69+
}
70+
Ok(())
71+
}
72+
RngSource::File => use_file(Some(256), dest),
9573
}
96-
97-
let ptr = FPTR.load(Ordering::SeqCst);
98-
unsafe { mem::transmute::<usize, Option<GetRandomFn>>(ptr) }
9974
}
10075

10176
#[inline(always)]

src/use_file.rs

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,50 +6,19 @@
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
88

9-
//! Implementation for DragonFly / Haiku
9+
//! Implementations that just need to read from a file
1010
extern crate std;
1111

12-
use crate::Error;
13-
use crate::utils::use_init;
14-
use std::{thread_local, io::Read, fs::File};
15-
use core::cell::RefCell;
12+
use crate::{utils::use_file, Error};
1613
use core::num::NonZeroU32;
1714

18-
thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));
19-
20-
#[cfg(target_os = "redox")]
21-
const FILE_PATH: &str = "rand:";
22-
#[cfg(target_os = "netbsd")]
23-
const FILE_PATH: &str = "/dev/urandom";
24-
#[cfg(any(target_os = "dragonfly", target_os = "emscripten", target_os = "haiku"))]
25-
const FILE_PATH: &str = "/dev/random";
26-
2715
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
28-
RNG_FILE.with(|f| {
29-
use_init(f, || init_file(), |f| use_file(f, dest))
30-
})
31-
}
32-
33-
fn use_file(f: &mut File, dest: &mut [u8]) -> Result<(), Error> {
16+
// `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes.
3417
if cfg!(target_os = "emscripten") {
35-
// `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes.
36-
for chunk in dest.chunks_mut(65536) {
37-
f.read_exact(chunk)?;
38-
}
18+
use_file(Some(65536), dest)
3919
} else {
40-
f.read_exact(dest)?;
41-
}
42-
core::mem::forget(f);
43-
Ok(())
44-
}
45-
46-
fn init_file() -> Result<File, Error> {
47-
if cfg!(target_os = "netbsd") {
48-
// read one byte from "/dev/random" to ensure that OS RNG has initialized
49-
File::open("/dev/random")?.read_exact(&mut [0u8; 1])?;
20+
use_file(None, dest)
5021
}
51-
let f = File::open(FILE_PATH)?;
52-
Ok(f)
5322
}
5423

5524
#[inline(always)]

src/utils.rs

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,44 @@
55
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
8+
9+
//! Helper function for reading random data from a file.
10+
extern crate std;
11+
812
use crate::Error;
9-
use core::cell::RefCell;
10-
use core::ops::DerefMut;
13+
use std::{
14+
fs::File,
15+
io::Read,
16+
os::unix::io::{FromRawFd, IntoRawFd, RawFd},
17+
};
18+
use lazy_static::lazy_static;
1119

12-
/// If `f` contains `Some(T)` call `use_f` using contents of `f` as an argument,
13-
/// otherwise initialize `f` value using `init_f`, store resulting value in `f`
14-
/// and call `use_f`.
15-
pub(crate) fn use_init<T, F, U>(f: &RefCell<Option<T>>, init_f: F, mut use_f: U)
16-
-> Result<(), Error>
17-
where F: FnOnce() -> Result<T, Error>, U: FnMut(&mut T) -> Result<(), Error>
18-
{
19-
let mut f = f.borrow_mut();
20-
let f: &mut Option<T> = f.deref_mut();
21-
match f {
22-
None => *f = Some(init_f()?),
23-
_ => (),
24-
}
20+
#[cfg(target_os = "redox")]
21+
const FILE_PATH: &str = "rand:";
22+
#[cfg(any(target_os = "netbsd", target_os = "linux", target_os = "android", target_os = "solaris", target_os = "illumos"))]
23+
const FILE_PATH: &str = "/dev/urandom";
24+
#[cfg(any(target_os = "dragonfly", target_os = "emscripten", target_os = "haiku"))]
25+
const FILE_PATH: &str = "/dev/random";
26+
27+
lazy_static! {
28+
static ref RNG_FILE: Result<RawFd, Error> = {
29+
if cfg!(target_os = "netbsd") || cfg!(target_os = "linux") || cfg!(target_os = "android") {
30+
// read one byte from "/dev/random" to ensure that OS RNG has initialized
31+
File::open("/dev/random")?.read_exact(&mut [0u8; 1])?;
32+
}
33+
Ok(File::open(FILE_PATH)?.into_raw_fd())
34+
};
35+
}
36+
37+
pub(crate) fn use_file(chunk_len: Option<usize>, dest: &mut [u8]) -> Result<(), Error> {
38+
let mut f = unsafe { File::from_raw_fd((*RNG_FILE)?) };
2539

26-
match f {
27-
Some(f) => use_f(f),
28-
None => unreachable!(),
40+
if let Some(chunk_len) = chunk_len {
41+
for chunk in dest.chunks_mut(chunk_len) {
42+
f.read_exact(chunk)?;
43+
}
44+
} else {
45+
f.read_exact(dest)?;
2946
}
47+
Ok(())
3048
}

0 commit comments

Comments
 (0)