Remove TLS by using lazy_static (#25)
This commit is contained in:
committed by
Artyom Pavlov
parent
a91b60b475
commit
af4ea8dc47
@@ -22,6 +22,7 @@ log = { version = "0.4", optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2.34"
|
||||
lazy_static = "1.3.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.6", features = ["minwindef", "ntsecapi", "winnt"] }
|
||||
@@ -32,9 +33,13 @@ cloudabi = "0.0.3"
|
||||
[target.'cfg(fuchsia)'.dependencies]
|
||||
fuchsia-cprng = "0.1"
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
lazy_static = "1.3.0"
|
||||
|
||||
[target.wasm32-unknown-unknown.dependencies]
|
||||
wasm-bindgen = { version = "0.2.29", optional = true }
|
||||
stdweb = { version = "0.4.9", optional = true }
|
||||
lazy_static = "1.3.0"
|
||||
|
||||
[target.wasm32-wasi.dependencies]
|
||||
libc = "0.2.54"
|
||||
|
||||
+11
-21
@@ -132,21 +132,6 @@ macro_rules! error { ($($x:tt)*) => () }
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "netbsd",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
target_os = "redox",
|
||||
target_os = "dragonfly",
|
||||
target_os = "haiku",
|
||||
target_os = "linux",
|
||||
all(
|
||||
target_arch = "wasm32",
|
||||
not(target_os = "wasi")
|
||||
),
|
||||
))]
|
||||
mod utils;
|
||||
mod error;
|
||||
pub use crate::error::Error;
|
||||
|
||||
@@ -172,6 +157,15 @@ macro_rules! mod_use {
|
||||
))]
|
||||
mod error_impls;
|
||||
|
||||
// These targets read from a file as a fallback method.
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "linux",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
))]
|
||||
mod use_file;
|
||||
|
||||
mod_use!(cfg(target_os = "android"), linux_android);
|
||||
mod_use!(cfg(target_os = "bitrig"), openbsd_bitrig);
|
||||
mod_use!(cfg(target_os = "cloudabi"), cloudabi);
|
||||
@@ -233,16 +227,12 @@ mod_use!(
|
||||
target_os = "redox",
|
||||
target_os = "solaris",
|
||||
all(target_arch = "x86_64", target_os = "uefi"),
|
||||
target_os = "wasi",
|
||||
target_env = "sgx",
|
||||
windows,
|
||||
all(
|
||||
target_arch = "wasm32",
|
||||
any(
|
||||
target_os = "emscripten",
|
||||
target_os = "wasi",
|
||||
feature = "wasm-bindgen",
|
||||
feature = "stdweb",
|
||||
),
|
||||
any(feature = "wasm-bindgen", feature = "stdweb"),
|
||||
),
|
||||
))),
|
||||
dummy
|
||||
|
||||
+21
-59
@@ -9,24 +9,10 @@
|
||||
//! Implementation for Linux / Android
|
||||
extern crate std;
|
||||
|
||||
use crate::Error;
|
||||
use crate::utils::use_init;
|
||||
use std::{thread_local, io::{self, Read}, fs::File};
|
||||
use core::cell::RefCell;
|
||||
use crate::{use_file, Error};
|
||||
use core::num::NonZeroU32;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
// This flag tells getrandom() to return EAGAIN instead of blocking.
|
||||
static RNG_INIT: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
enum RngSource {
|
||||
GetRandom,
|
||||
Device(File),
|
||||
}
|
||||
|
||||
thread_local!(
|
||||
static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
|
||||
);
|
||||
use lazy_static::lazy_static;
|
||||
use std::io;
|
||||
|
||||
fn syscall_getrandom(dest: &mut [u8], block: bool) -> Result<usize, io::Error> {
|
||||
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> {
|
||||
}
|
||||
|
||||
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
|
||||
RNG_SOURCE.with(|f| {
|
||||
use_init(f,
|
||||
|| {
|
||||
let s = if is_getrandom_available() {
|
||||
RngSource::GetRandom
|
||||
} else {
|
||||
// read one byte from "/dev/random" to ensure that
|
||||
// OS RNG has initialized
|
||||
if !RNG_INIT.load(Ordering::Relaxed) {
|
||||
File::open("/dev/random")?.read_exact(&mut [0u8; 1])?;
|
||||
RNG_INIT.store(true, Ordering::Relaxed)
|
||||
}
|
||||
RngSource::Device(File::open("/dev/urandom")?)
|
||||
};
|
||||
Ok(s)
|
||||
}, |f| {
|
||||
match f {
|
||||
RngSource::GetRandom => {
|
||||
let mut start = 0;
|
||||
while start < dest.len() {
|
||||
start += syscall_getrandom(&mut dest[start..], true)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
RngSource::Device(f) => f.read_exact(dest).map_err(From::from),
|
||||
lazy_static! { static ref HAS_GETRANDOM: bool = is_getrandom_available(); }
|
||||
match *HAS_GETRANDOM {
|
||||
true => {
|
||||
let mut start = 0;
|
||||
while start < dest.len() {
|
||||
start += syscall_getrandom(&mut dest[start..], true)?;
|
||||
}
|
||||
})
|
||||
})
|
||||
Ok(())
|
||||
},
|
||||
false => use_file::getrandom_inner(dest),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_getrandom_available() -> bool {
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
|
||||
static CHECKER: Once = ONCE_INIT;
|
||||
static AVAILABLE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
CHECKER.call_once(|| {
|
||||
let mut buf: [u8; 0] = [];
|
||||
let available = match syscall_getrandom(&mut buf, false) {
|
||||
Ok(_) => true,
|
||||
Err(err) => err.raw_os_error() != Some(libc::ENOSYS),
|
||||
};
|
||||
AVAILABLE.store(available, Ordering::Relaxed);
|
||||
});
|
||||
|
||||
AVAILABLE.load(Ordering::Relaxed)
|
||||
match syscall_getrandom(&mut [], false) {
|
||||
Err(err) => match err.raw_os_error() {
|
||||
Some(libc::ENOSYS) => false, // No kernel support
|
||||
Some(libc::EPERM) => false, // Blocked by seccomp
|
||||
_ => true,
|
||||
}
|
||||
Ok(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
||||
+16
-53
@@ -19,26 +19,17 @@
|
||||
//! libc::dlsym.
|
||||
extern crate std;
|
||||
|
||||
use crate::Error;
|
||||
use crate::utils::use_init;
|
||||
use std::{thread_local, io::{self, Read}, fs::File};
|
||||
use core::cell::RefCell;
|
||||
use crate::{use_file, Error};
|
||||
use core::mem;
|
||||
use core::num::NonZeroU32;
|
||||
use lazy_static::lazy_static;
|
||||
use std::io;
|
||||
|
||||
#[cfg(target_os = "illumos")]
|
||||
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
|
||||
#[cfg(target_os = "solaris")]
|
||||
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::c_int;
|
||||
|
||||
enum RngSource {
|
||||
GetRandom(GetRandomFn),
|
||||
Device(File),
|
||||
}
|
||||
|
||||
thread_local!(
|
||||
static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
|
||||
);
|
||||
|
||||
fn libc_getrandom(rand: GetRandomFn, dest: &mut [u8]) -> Result<(), Error> {
|
||||
let ret = unsafe { rand(dest.as_mut_ptr(), dest.len(), 0) as libc::ssize_t };
|
||||
|
||||
@@ -51,51 +42,23 @@ fn libc_getrandom(rand: GetRandomFn, dest: &mut [u8]) -> Result<(), Error> {
|
||||
}
|
||||
|
||||
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
|
||||
lazy_static! { static ref GETRANDOM_FUNC: Option<GetRandomFn> = fetch_getrandom(); }
|
||||
|
||||
// 256 bytes is the lowest common denominator across all the Solaris
|
||||
// derived platforms for atomically obtaining random data.
|
||||
RNG_SOURCE.with(|f| {
|
||||
use_init(
|
||||
f,
|
||||
|| {
|
||||
let s = match fetch_getrandom() {
|
||||
Some(fptr) => RngSource::GetRandom(fptr),
|
||||
None => RngSource::Device(File::open("/dev/random")?),
|
||||
};
|
||||
Ok(s)
|
||||
},
|
||||
|f| {
|
||||
match f {
|
||||
RngSource::GetRandom(rp) => {
|
||||
for chunk in dest.chunks_mut(256) {
|
||||
libc_getrandom(*rp, chunk)?
|
||||
}
|
||||
}
|
||||
RngSource::Device(randf) => {
|
||||
for chunk in dest.chunks_mut(256) {
|
||||
randf.read_exact(chunk)?
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
})
|
||||
for chunk in dest.chunks_mut(256) {
|
||||
match *GETRANDOM_FUNC {
|
||||
Some(fptr) => libc_getrandom(fptr, chunk)?,
|
||||
None => use_file::getrandom_inner(chunk)?,
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fetch_getrandom() -> Option<GetRandomFn> {
|
||||
use std::mem;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
static FPTR: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
if FPTR.load(Ordering::SeqCst) == 1 {
|
||||
let name = "getrandom\0";
|
||||
let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) as usize };
|
||||
FPTR.store(addr, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
let ptr = FPTR.load(Ordering::SeqCst);
|
||||
unsafe { mem::transmute::<usize, Option<GetRandomFn>>(ptr) }
|
||||
let name = "getrandom\0";
|
||||
let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) };
|
||||
unsafe { mem::transmute(addr) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
||||
+21
-18
@@ -6,31 +6,35 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Implementation for DragonFly / Haiku
|
||||
//! Implementations that just need to read from a file
|
||||
extern crate std;
|
||||
|
||||
use crate::Error;
|
||||
use crate::utils::use_init;
|
||||
use std::{thread_local, io::Read, fs::File};
|
||||
use core::cell::RefCell;
|
||||
use core::num::NonZeroU32;
|
||||
|
||||
thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));
|
||||
use lazy_static::lazy_static;
|
||||
use std::{
|
||||
fs::File,
|
||||
io::Read,
|
||||
os::unix::io::{FromRawFd, IntoRawFd, RawFd},
|
||||
};
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
const FILE_PATH: &str = "rand:";
|
||||
#[cfg(target_os = "netbsd")]
|
||||
#[cfg(any(target_os = "android", target_os = "linux", target_os = "netbsd"))]
|
||||
const FILE_PATH: &str = "/dev/urandom";
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "emscripten", target_os = "haiku"))]
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "emscripten",
|
||||
target_os = "haiku",
|
||||
target_os = "solaris",
|
||||
target_os = "illumos"
|
||||
))]
|
||||
const FILE_PATH: &str = "/dev/random";
|
||||
|
||||
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
|
||||
RNG_FILE.with(|f| {
|
||||
use_init(f, || init_file(), |f| use_file(f, dest))
|
||||
})
|
||||
}
|
||||
lazy_static! { static ref RNG_FD: Result<RawFd, Error> = init_file(); }
|
||||
let mut f = unsafe { File::from_raw_fd((*RNG_FD)?) };
|
||||
|
||||
fn use_file(f: &mut File, dest: &mut [u8]) -> Result<(), Error> {
|
||||
if cfg!(target_os = "emscripten") {
|
||||
// `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes.
|
||||
for chunk in dest.chunks_mut(65536) {
|
||||
@@ -39,18 +43,17 @@ fn use_file(f: &mut File, dest: &mut [u8]) -> Result<(), Error> {
|
||||
} else {
|
||||
f.read_exact(dest)?;
|
||||
}
|
||||
core::mem::forget(f);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_file() -> Result<File, Error> {
|
||||
if cfg!(target_os = "netbsd") {
|
||||
fn init_file() -> Result<RawFd, Error> {
|
||||
if FILE_PATH == "/dev/urandom" {
|
||||
// read one byte from "/dev/random" to ensure that OS RNG has initialized
|
||||
File::open("/dev/random")?.read_exact(&mut [0u8; 1])?;
|
||||
}
|
||||
let f = File::open(FILE_PATH)?;
|
||||
Ok(f)
|
||||
Ok(File::open(FILE_PATH)?.into_raw_fd())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { None }
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright 2018 Developers of the Rand project.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
use crate::Error;
|
||||
use core::cell::RefCell;
|
||||
use core::ops::DerefMut;
|
||||
|
||||
/// If `f` contains `Some(T)` call `use_f` using contents of `f` as an argument,
|
||||
/// otherwise initialize `f` value using `init_f`, store resulting value in `f`
|
||||
/// and call `use_f`.
|
||||
pub(crate) fn use_init<T, F, U>(f: &RefCell<Option<T>>, init_f: F, mut use_f: U)
|
||||
-> Result<(), Error>
|
||||
where F: FnOnce() -> Result<T, Error>, U: FnMut(&mut T) -> Result<(), Error>
|
||||
{
|
||||
let mut f = f.borrow_mut();
|
||||
let f: &mut Option<T> = f.deref_mut();
|
||||
match f {
|
||||
None => *f = Some(init_f()?),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match f {
|
||||
Some(f) => use_f(f),
|
||||
None => unreachable!(),
|
||||
}
|
||||
}
|
||||
+20
-19
@@ -7,8 +7,6 @@
|
||||
// except according to those terms.
|
||||
|
||||
//! Implementation for WASM via wasm-bindgen
|
||||
extern crate std;
|
||||
|
||||
use core::cell::RefCell;
|
||||
use core::mem;
|
||||
use core::num::NonZeroU32;
|
||||
@@ -18,7 +16,6 @@ use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::Error;
|
||||
use crate::error::CODE_PREFIX;
|
||||
use crate::utils::use_init;
|
||||
|
||||
const CODE_CRYPTO_UNDEF: u32 = CODE_PREFIX | 0x80;
|
||||
const CODE_GRV_UNDEF: u32 = CODE_PREFIX | 0x81;
|
||||
@@ -29,6 +26,8 @@ enum RngSource {
|
||||
Browser(BrowserCrypto),
|
||||
}
|
||||
|
||||
// JsValues are always per-thread, so we initialize RngSource for each thread.
|
||||
// See: https://github.com/rustwasm/wasm-bindgen/pull/955
|
||||
thread_local!(
|
||||
static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
|
||||
);
|
||||
@@ -37,25 +36,27 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
|
||||
assert_eq!(mem::size_of::<usize>(), 4);
|
||||
|
||||
RNG_SOURCE.with(|f| {
|
||||
use_init(f, getrandom_init, |source| {
|
||||
match *source {
|
||||
RngSource::Node(ref n) => n.random_fill_sync(dest),
|
||||
RngSource::Browser(ref n) => {
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
|
||||
//
|
||||
// where it says:
|
||||
//
|
||||
// > A QuotaExceededError DOMException is thrown if the
|
||||
// > requested length is greater than 65536 bytes.
|
||||
for chunk in dest.chunks_mut(65536) {
|
||||
n.get_random_values(chunk)
|
||||
}
|
||||
let mut source = f.borrow_mut();
|
||||
if source.is_none() {
|
||||
*source = Some(getrandom_init()?);
|
||||
}
|
||||
|
||||
match source.as_ref().unwrap() {
|
||||
RngSource::Node(n) => n.random_fill_sync(dest),
|
||||
RngSource::Browser(n) => {
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
|
||||
//
|
||||
// where it says:
|
||||
//
|
||||
// > A QuotaExceededError DOMException is thrown if the
|
||||
// > requested length is greater than 65536 bytes.
|
||||
for chunk in dest.chunks_mut(65536) {
|
||||
n.get_random_values(chunk)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
};
|
||||
Ok(())
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
fn getrandom_init() -> Result<RngSource, Error> {
|
||||
|
||||
+5
-13
@@ -7,35 +7,27 @@
|
||||
// except according to those terms.
|
||||
|
||||
//! Implementation for WASM via stdweb
|
||||
use core::cell::RefCell;
|
||||
use core::mem;
|
||||
use core::num::NonZeroU32;
|
||||
use std::thread_local;
|
||||
|
||||
use stdweb::{js, _js_impl};
|
||||
use stdweb::unstable::TryInto;
|
||||
use stdweb::web::error::Error as WebError;
|
||||
|
||||
use crate::Error;
|
||||
use crate::utils::use_init;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum RngSource {
|
||||
Browser,
|
||||
Node
|
||||
}
|
||||
|
||||
thread_local!(
|
||||
static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
|
||||
);
|
||||
|
||||
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
|
||||
assert_eq!(mem::size_of::<usize>(), 4);
|
||||
|
||||
RNG_SOURCE.with(|f| {
|
||||
use_init(f, getrandom_init, |source| getrandom_fill(source, dest))
|
||||
})
|
||||
|
||||
lazy_static! { static ref RNG_SOURCE: Result<RngSource, Error> = getrandom_init(); }
|
||||
getrandom_fill((*RNG_SOURCE)?, dest)
|
||||
}
|
||||
|
||||
fn getrandom_init() -> Result<RngSource, Error> {
|
||||
@@ -72,7 +64,7 @@ fn getrandom_init() -> Result<RngSource, Error> {
|
||||
}
|
||||
}
|
||||
|
||||
fn getrandom_fill(source: &mut RngSource, dest: &mut [u8]) -> Result<(), Error> {
|
||||
fn getrandom_fill(source: RngSource, dest: &mut [u8]) -> Result<(), Error> {
|
||||
for chunk in dest.chunks_mut(65536) {
|
||||
let len = chunk.len() as u32;
|
||||
let ptr = chunk.as_mut_ptr() as i32;
|
||||
|
||||
Reference in New Issue
Block a user