Skip to content

Commit af4ea8d

Browse files
josephlrnewpavlov
authored andcommitted
Remove TLS by using lazy_static (#25)
1 parent a91b60b commit af4ea8d

8 files changed

+99
-213
lines changed

Cargo.toml

+5
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.34"
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

+11-21
Original file line numberDiff line numberDiff line change
@@ -132,21 +132,6 @@ 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-
))]
149-
mod utils;
150135
mod error;
151136
pub use crate::error::Error;
152137

@@ -172,6 +157,15 @@ macro_rules! mod_use {
172157
))]
173158
mod error_impls;
174159

160+
// These targets read from a file as a fallback method.
161+
#[cfg(any(
162+
target_os = "android",
163+
target_os = "linux",
164+
target_os = "solaris",
165+
target_os = "illumos",
166+
))]
167+
mod use_file;
168+
175169
mod_use!(cfg(target_os = "android"), linux_android);
176170
mod_use!(cfg(target_os = "bitrig"), openbsd_bitrig);
177171
mod_use!(cfg(target_os = "cloudabi"), cloudabi);
@@ -233,16 +227,12 @@ mod_use!(
233227
target_os = "redox",
234228
target_os = "solaris",
235229
all(target_arch = "x86_64", target_os = "uefi"),
230+
target_os = "wasi",
236231
target_env = "sgx",
237232
windows,
238233
all(
239234
target_arch = "wasm32",
240-
any(
241-
target_os = "emscripten",
242-
target_os = "wasi",
243-
feature = "wasm-bindgen",
244-
feature = "stdweb",
245-
),
235+
any(feature = "wasm-bindgen", feature = "stdweb"),
246236
),
247237
))),
248238
dummy

src/linux_android.rs

+21-59
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,10 @@
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::{use_file, Error};
1613
use core::num::NonZeroU32;
17-
use core::sync::atomic::{AtomicBool, Ordering};
18-
19-
// This flag tells getrandom() to return EAGAIN instead of blocking.
20-
static RNG_INIT: AtomicBool = AtomicBool::new(false);
21-
22-
enum RngSource {
23-
GetRandom,
24-
Device(File),
25-
}
26-
27-
thread_local!(
28-
static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
29-
);
14+
use lazy_static::lazy_static;
15+
use std::io;
3016

3117
fn syscall_getrandom(dest: &mut [u8], block: bool) -> Result<usize, io::Error> {
3218
let flags = if block { 0 } else { libc::GRND_NONBLOCK };
@@ -45,52 +31,28 @@ fn syscall_getrandom(dest: &mut [u8], block: bool) -> Result<usize, io::Error> {
4531
}
4632

4733
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
48-
RNG_SOURCE.with(|f| {
49-
use_init(f,
50-
|| {
51-
let s = if is_getrandom_available() {
52-
RngSource::GetRandom
53-
} else {
54-
// read one byte from "/dev/random" to ensure that
55-
// OS RNG has initialized
56-
if !RNG_INIT.load(Ordering::Relaxed) {
57-
File::open("/dev/random")?.read_exact(&mut [0u8; 1])?;
58-
RNG_INIT.store(true, Ordering::Relaxed)
59-
}
60-
RngSource::Device(File::open("/dev/urandom")?)
61-
};
62-
Ok(s)
63-
}, |f| {
64-
match f {
65-
RngSource::GetRandom => {
66-
let mut start = 0;
67-
while start < dest.len() {
68-
start += syscall_getrandom(&mut dest[start..], true)?;
69-
}
70-
Ok(())
71-
}
72-
RngSource::Device(f) => f.read_exact(dest).map_err(From::from),
34+
lazy_static! { static ref HAS_GETRANDOM: bool = is_getrandom_available(); }
35+
match *HAS_GETRANDOM {
36+
true => {
37+
let mut start = 0;
38+
while start < dest.len() {
39+
start += syscall_getrandom(&mut dest[start..], true)?;
7340
}
74-
})
75-
})
41+
Ok(())
42+
},
43+
false => use_file::getrandom_inner(dest),
44+
}
7645
}
7746

7847
fn is_getrandom_available() -> bool {
79-
use std::sync::{Once, ONCE_INIT};
80-
81-
static CHECKER: Once = ONCE_INIT;
82-
static AVAILABLE: AtomicBool = AtomicBool::new(false);
83-
84-
CHECKER.call_once(|| {
85-
let mut buf: [u8; 0] = [];
86-
let available = match syscall_getrandom(&mut buf, false) {
87-
Ok(_) => true,
88-
Err(err) => err.raw_os_error() != Some(libc::ENOSYS),
89-
};
90-
AVAILABLE.store(available, Ordering::Relaxed);
91-
});
92-
93-
AVAILABLE.load(Ordering::Relaxed)
48+
match syscall_getrandom(&mut [], false) {
49+
Err(err) => match err.raw_os_error() {
50+
Some(libc::ENOSYS) => false, // No kernel support
51+
Some(libc::EPERM) => false, // Blocked by seccomp
52+
_ => true,
53+
}
54+
Ok(_) => true,
55+
}
9456
}
9557

9658
#[inline(always)]

src/solaris_illumos.rs

+16-53
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,17 @@
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::{use_file, Error};
23+
use core::mem;
2624
use core::num::NonZeroU32;
25+
use lazy_static::lazy_static;
26+
use std::io;
2727

2828
#[cfg(target_os = "illumos")]
2929
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
3030
#[cfg(target_os = "solaris")]
3131
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::c_int;
3232

33-
enum RngSource {
34-
GetRandom(GetRandomFn),
35-
Device(File),
36-
}
37-
38-
thread_local!(
39-
static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
40-
);
41-
4233
fn libc_getrandom(rand: GetRandomFn, dest: &mut [u8]) -> Result<(), Error> {
4334
let ret = unsafe { rand(dest.as_mut_ptr(), dest.len(), 0) as libc::ssize_t };
4435

@@ -51,51 +42,23 @@ fn libc_getrandom(rand: GetRandomFn, dest: &mut [u8]) -> Result<(), Error> {
5142
}
5243

5344
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
45+
lazy_static! { static ref GETRANDOM_FUNC: Option<GetRandomFn> = fetch_getrandom(); }
46+
5447
// 256 bytes is the lowest common denominator across all the Solaris
5548
// 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-
})
49+
for chunk in dest.chunks_mut(256) {
50+
match *GETRANDOM_FUNC {
51+
Some(fptr) => libc_getrandom(fptr, chunk)?,
52+
None => use_file::getrandom_inner(chunk)?,
53+
};
54+
}
55+
Ok(())
8356
}
8457

8558
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);
95-
}
96-
97-
let ptr = FPTR.load(Ordering::SeqCst);
98-
unsafe { mem::transmute::<usize, Option<GetRandomFn>>(ptr) }
59+
let name = "getrandom\0";
60+
let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) };
61+
unsafe { mem::transmute(addr) }
9962
}
10063

10164
#[inline(always)]

src/use_file.rs

+21-18
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,35 @@
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

1212
use crate::Error;
13-
use crate::utils::use_init;
14-
use std::{thread_local, io::Read, fs::File};
15-
use core::cell::RefCell;
1613
use core::num::NonZeroU32;
17-
18-
thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));
14+
use lazy_static::lazy_static;
15+
use std::{
16+
fs::File,
17+
io::Read,
18+
os::unix::io::{FromRawFd, IntoRawFd, RawFd},
19+
};
1920

2021
#[cfg(target_os = "redox")]
2122
const FILE_PATH: &str = "rand:";
22-
#[cfg(target_os = "netbsd")]
23+
#[cfg(any(target_os = "android", target_os = "linux", target_os = "netbsd"))]
2324
const FILE_PATH: &str = "/dev/urandom";
24-
#[cfg(any(target_os = "dragonfly", target_os = "emscripten", target_os = "haiku"))]
25+
#[cfg(any(
26+
target_os = "dragonfly",
27+
target_os = "emscripten",
28+
target_os = "haiku",
29+
target_os = "solaris",
30+
target_os = "illumos"
31+
))]
2532
const FILE_PATH: &str = "/dev/random";
2633

2734
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-
}
35+
lazy_static! { static ref RNG_FD: Result<RawFd, Error> = init_file(); }
36+
let mut f = unsafe { File::from_raw_fd((*RNG_FD)?) };
3237

33-
fn use_file(f: &mut File, dest: &mut [u8]) -> Result<(), Error> {
3438
if cfg!(target_os = "emscripten") {
3539
// `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes.
3640
for chunk in dest.chunks_mut(65536) {
@@ -39,18 +43,17 @@ fn use_file(f: &mut File, dest: &mut [u8]) -> Result<(), Error> {
3943
} else {
4044
f.read_exact(dest)?;
4145
}
42-
core::mem::forget(f);
4346
Ok(())
4447
}
4548

46-
fn init_file() -> Result<File, Error> {
47-
if cfg!(target_os = "netbsd") {
49+
fn init_file() -> Result<RawFd, Error> {
50+
if FILE_PATH == "/dev/urandom" {
4851
// read one byte from "/dev/random" to ensure that OS RNG has initialized
4952
File::open("/dev/random")?.read_exact(&mut [0u8; 1])?;
5053
}
51-
let f = File::open(FILE_PATH)?;
52-
Ok(f)
54+
Ok(File::open(FILE_PATH)?.into_raw_fd())
5355
}
5456

5557
#[inline(always)]
58+
#[allow(dead_code)]
5659
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { None }

src/utils.rs

-30
This file was deleted.

0 commit comments

Comments
 (0)