Skip to content

Commit add0fb0

Browse files
committed
Merge branch 'master' into 0.2
Signed-off-by: Joe Richey <joerichey@google.com>
2 parents 60bec7a + 481420e commit add0fb0

8 files changed

Lines changed: 159 additions & 120 deletions

File tree

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ matrix:
5050
- gunzip cargo-web.gz
5151
- chmod +x cargo-web
5252
# Get wasmtime
53-
- export VERSION=v0.3.0 # Pin version for stability
53+
- export VERSION=v0.8.0 # Pin version for stability
5454
- wget -O wasmtime.tar.xz https://github.com/CraneStation/wasmtime/releases/download/$VERSION/wasmtime-$VERSION-x86_64-linux.tar.xz
5555
- tar -xf wasmtime.tar.xz --strip-components=1
5656
# Get wasm-bindgen-test-runner which matches our wasm-bindgen version

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,36 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.1.14] - 2020-01-07
8+
### Changed
9+
- Remove use of spin-locks in the `use_file` module. [#125]
10+
- Update `wasi` to v0.9. [#126]
11+
- Do not read errno value on DragonFlyBSD to fix compilation failure. [#129]
12+
13+
[#125]: https://github.com/rust-random/getrandom/pull/125
14+
[#126]: https://github.com/rust-random/getrandom/pull/126
15+
[#129]: https://github.com/rust-random/getrandom/pull/129
16+
17+
## [0.1.13] - 2019-08-25
18+
### Added
19+
- VxWorks targets support. [#86]
20+
21+
### Changed
22+
- If zero-length slice is passed to the `getrandom` function, always return
23+
`Ok(())` immediately without doing any calls to the underlying operating
24+
system. [#104]
25+
- Use the `kern.arandom` sysctl on NetBSD. [#115]
26+
27+
### Fixed
28+
- Bump `cfg-if` minimum version from 0.1.0 to 0.1.2. [#112]
29+
- Typos and bad doc links. [#117]
30+
31+
[#86]: https://github.com/rust-random/getrandom/pull/86
32+
[#104]: https://github.com/rust-random/getrandom/pull/104
33+
[#112]: https://github.com/rust-random/getrandom/pull/112
34+
[#115]: https://github.com/rust-random/getrandom/pull/115
35+
[#117]: https://github.com/rust-random/getrandom/pull/117
36+
737
## [0.1.12] - 2019-08-18
838
### Changed
939
- Update wasi dependency from v0.5 to v0.7. [#100]

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ core = { version = "1.0", optional = true, package = "rustc-std-workspace-core"
2626
libc = { version = "0.2.64", default-features = false }
2727

2828
[target.'cfg(target_os = "wasi")'.dependencies]
29-
wasi = "0.7"
29+
wasi = "0.9"
3030

3131
[target.wasm32-unknown-unknown.dependencies]
3232
wasm-bindgen = { version = "0.2.29", optional = true }

src/error.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl Error {
8787

8888
cfg_if! {
8989
if #[cfg(unix)] {
90-
fn os_err_desc(errno: i32, buf: &mut [u8]) -> Option<&str> {
90+
fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> {
9191
let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char;
9292
if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 {
9393
return None;
@@ -99,12 +99,11 @@ cfg_if! {
9999
core::str::from_utf8(&buf[..idx]).ok()
100100
}
101101
} else if #[cfg(target_os = "wasi")] {
102-
fn os_err_desc(errno: i32, _buf: &mut [u8]) -> Option<&str> {
103-
core::num::NonZeroU16::new(errno as u16)
104-
.and_then(wasi::wasi_unstable::error_str)
102+
fn os_err(errno: i32, _buf: &mut [u8]) -> Option<wasi::Error> {
103+
wasi::Error::from_raw_error(errno as _)
105104
}
106105
} else {
107-
fn os_err_desc(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
106+
fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
108107
None
109108
}
110109
}
@@ -116,8 +115,8 @@ impl fmt::Debug for Error {
116115
if let Some(errno) = self.raw_os_error() {
117116
dbg.field("os_error", &errno);
118117
let mut buf = [0u8; 128];
119-
if let Some(desc) = os_err_desc(errno, &mut buf) {
120-
dbg.field("description", &desc);
118+
if let Some(err) = os_err(errno, &mut buf) {
119+
dbg.field("description", &err);
121120
}
122121
} else if let Some(desc) = internal_desc(*self) {
123122
dbg.field("internal_code", &self.0.get());
@@ -133,8 +132,8 @@ impl fmt::Display for Error {
133132
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134133
if let Some(errno) = self.raw_os_error() {
135134
let mut buf = [0u8; 128];
136-
match os_err_desc(errno, &mut buf) {
137-
Some(desc) => f.write_str(desc),
135+
match os_err(errno, &mut buf) {
136+
Some(err) => err.fmt(f),
138137
None => write!(f, "OS Error: {}", errno),
139138
}
140139
} else if let Some(desc) = internal_desc(*self) {

src/use_file.rs

Lines changed: 96 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
// except according to those terms.
88

99
//! Implementations that just need to read from a file
10-
use crate::util_libc::{last_os_error, open_readonly, sys_fill_exact, LazyFd};
10+
use crate::util::LazyUsize;
11+
use crate::util_libc::{open_readonly, sys_fill_exact};
1112
use crate::Error;
13+
use core::cell::UnsafeCell;
14+
use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
1215

1316
#[cfg(target_os = "redox")]
1417
const FILE_PATH: &str = "rand:\0";
@@ -21,10 +24,11 @@ const FILE_PATH: &str = "rand:\0";
2124
target_os = "illumos"
2225
))]
2326
const FILE_PATH: &str = "/dev/random\0";
27+
#[cfg(any(target_os = "android", target_os = "linux"))]
28+
const FILE_PATH: &str = "/dev/urandom\0";
2429

2530
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
26-
static FD: LazyFd = LazyFd::new();
27-
let fd = FD.init(init_file).ok_or_else(last_os_error)?;
31+
let fd = get_rng_fd()?;
2832
let read = |buf: &mut [u8]| unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) };
2933

3034
if cfg!(target_os = "emscripten") {
@@ -38,36 +42,96 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
3842
Ok(())
3943
}
4044

41-
cfg_if! {
42-
if #[cfg(any(target_os = "android", target_os = "linux"))] {
43-
fn init_file() -> Option<libc::c_int> {
44-
// Poll /dev/random to make sure it is ok to read from /dev/urandom.
45-
let mut pfd = libc::pollfd {
46-
fd: unsafe { open_readonly("/dev/random\0")? },
47-
events: libc::POLLIN,
48-
revents: 0,
49-
};
50-
51-
let ret = loop {
52-
// A negative timeout means an infinite timeout.
53-
let res = unsafe { libc::poll(&mut pfd, 1, -1) };
54-
if res == 1 {
55-
break unsafe { open_readonly("/dev/urandom\0") };
56-
} else if res < 0 {
57-
let e = last_os_error().raw_os_error();
58-
if e == Some(libc::EINTR) || e == Some(libc::EAGAIN) {
59-
continue;
60-
}
61-
}
62-
// We either hard failed, or poll() returned the wrong pfd.
63-
break None;
64-
};
65-
unsafe { libc::close(pfd.fd) };
66-
ret
45+
// Returns the file descriptor for the device file used to retrieve random
46+
// bytes. The file will be opened exactly once. All successful calls will
47+
// return the same file descriptor. This file descriptor is never closed.
48+
fn get_rng_fd() -> Result<libc::c_int, Error> {
49+
static FD: AtomicUsize = AtomicUsize::new(LazyUsize::UNINIT);
50+
fn get_fd() -> Option<libc::c_int> {
51+
match FD.load(Relaxed) {
52+
LazyUsize::UNINIT => None,
53+
val => Some(val as libc::c_int),
6754
}
68-
} else {
69-
fn init_file() -> Option<libc::c_int> {
70-
unsafe { open_readonly(FILE_PATH) }
55+
}
56+
57+
// Use double-checked locking to avoid acquiring the lock if possible.
58+
if let Some(fd) = get_fd() {
59+
return Ok(fd);
60+
}
61+
62+
// SAFETY: We use the mutex only in this method, and we always unlock it
63+
// before returning, making sure we don't violate the pthread_mutex_t API.
64+
static MUTEX: Mutex = Mutex::new();
65+
unsafe { MUTEX.lock() };
66+
let _guard = DropGuard(|| unsafe { MUTEX.unlock() });
67+
68+
if let Some(fd) = get_fd() {
69+
return Ok(fd);
70+
}
71+
72+
// On Linux, /dev/urandom might return insecure values.
73+
#[cfg(any(target_os = "android", target_os = "linux"))]
74+
wait_until_rng_ready()?;
75+
76+
let fd = unsafe { open_readonly(FILE_PATH)? };
77+
// The fd always fits in a usize without conflicting with UNINIT.
78+
debug_assert!(fd >= 0 && (fd as usize) < LazyUsize::UNINIT);
79+
FD.store(fd as usize, Relaxed);
80+
81+
Ok(fd)
82+
}
83+
84+
// Succeeds once /dev/urandom is safe to read from
85+
#[cfg(any(target_os = "android", target_os = "linux"))]
86+
fn wait_until_rng_ready() -> Result<(), Error> {
87+
// Poll /dev/random to make sure it is ok to read from /dev/urandom.
88+
let fd = unsafe { open_readonly("/dev/random\0")? };
89+
let mut pfd = libc::pollfd {
90+
fd,
91+
events: libc::POLLIN,
92+
revents: 0,
93+
};
94+
let _guard = DropGuard(|| unsafe {
95+
libc::close(fd);
96+
});
97+
98+
loop {
99+
// A negative timeout means an infinite timeout.
100+
let res = unsafe { libc::poll(&mut pfd, 1, -1) };
101+
if res >= 0 {
102+
assert_eq!(res, 1); // We only used one fd, and cannot timeout.
103+
return Ok(());
71104
}
105+
let err = crate::util_libc::last_os_error();
106+
match err.raw_os_error() {
107+
Some(libc::EINTR) | Some(libc::EAGAIN) => continue,
108+
_ => return Err(err),
109+
}
110+
}
111+
}
112+
113+
struct Mutex(UnsafeCell<libc::pthread_mutex_t>);
114+
115+
impl Mutex {
116+
const fn new() -> Self {
117+
Self(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))
118+
}
119+
unsafe fn lock(&self) {
120+
let r = libc::pthread_mutex_lock(self.0.get());
121+
debug_assert_eq!(r, 0);
122+
}
123+
unsafe fn unlock(&self) {
124+
let r = libc::pthread_mutex_unlock(self.0.get());
125+
debug_assert_eq!(r, 0);
126+
}
127+
}
128+
129+
unsafe impl Sync for Mutex {}
130+
131+
struct DropGuard<F: FnMut()>(F);
132+
133+
impl<F: FnMut()> Drop for DropGuard<F> {
134+
fn drop(&mut self) {
135+
self.0()
72136
}
73137
}

src/util.rs

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ impl LazyUsize {
3535

3636
// The initialization is not completed.
3737
pub const UNINIT: usize = usize::max_value();
38-
// The initialization is currently running.
39-
pub const ACTIVE: usize = usize::max_value() - 1;
4038

4139
// Runs the init() function at least once, returning the value of some run
4240
// of init(). Multiple callers can run their init() functions in parallel.
@@ -50,36 +48,6 @@ impl LazyUsize {
5048
}
5149
val
5250
}
53-
54-
// Synchronously runs the init() function. Only one caller will have their
55-
// init() function running at a time, and exactly one successful call will
56-
// be run. init() returning UNINIT or ACTIVE will be considered a failure,
57-
// and future calls to sync_init will rerun their init() function.
58-
pub fn sync_init(&self, init: impl FnOnce() -> usize, mut wait: impl FnMut()) -> usize {
59-
// Common and fast path with no contention. Don't wast time on CAS.
60-
match self.0.load(Relaxed) {
61-
Self::UNINIT | Self::ACTIVE => {}
62-
val => return val,
63-
}
64-
// Relaxed ordering is fine, as we only have a single atomic variable.
65-
loop {
66-
match self.0.compare_and_swap(Self::UNINIT, Self::ACTIVE, Relaxed) {
67-
Self::UNINIT => {
68-
let val = init();
69-
self.0.store(
70-
match val {
71-
Self::UNINIT | Self::ACTIVE => Self::UNINIT,
72-
val => val,
73-
},
74-
Relaxed,
75-
);
76-
return val;
77-
}
78-
Self::ACTIVE => wait(),
79-
val => return val,
80-
}
81-
}
82-
}
8351
}
8452

8553
// Identical to LazyUsize except with bool instead of usize.

src/util_libc.rs

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,27 @@ cfg_if! {
1818
use libc::__errno_location as errno_location;
1919
} else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] {
2020
use libc::___errno as errno_location;
21-
} else if #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly"))] {
21+
} else if #[cfg(any(target_os = "macos", target_os = "freebsd"))] {
2222
use libc::__error as errno_location;
2323
} else if #[cfg(target_os = "haiku")] {
2424
use libc::_errnop as errno_location;
2525
}
2626
}
2727

28+
cfg_if! {
29+
if #[cfg(target_os = "vxworks")] {
30+
use libc::errnoGet as get_errno;
31+
} else if #[cfg(target_os = "dragonfly")] {
32+
// Until rust-lang/rust#29594 is stable, we cannot get the errno value
33+
// on DragonFlyBSD. So we just return an out-of-range errno.
34+
unsafe fn get_errno() -> libc::c_int { -1 }
35+
} else {
36+
unsafe fn get_errno() -> libc::c_int { *errno_location() }
37+
}
38+
}
39+
2840
pub fn last_os_error() -> Error {
29-
#[cfg(not(target_os = "vxworks"))]
30-
let errno = unsafe { *errno_location() };
31-
#[cfg(target_os = "vxworks")]
32-
let errno = unsafe { libc::errnoGet() };
41+
let errno = unsafe { get_errno() };
3342
if errno > 0 {
3443
Error::from(NonZeroU32::new(errno as u32).unwrap())
3544
} else {
@@ -89,37 +98,6 @@ impl Weak {
8998
}
9099
}
91100

92-
pub struct LazyFd(LazyUsize);
93-
94-
impl LazyFd {
95-
pub const fn new() -> Self {
96-
Self(LazyUsize::new())
97-
}
98-
99-
// If init() returns Some(x), x should be nonnegative.
100-
pub fn init(&self, init: impl FnOnce() -> Option<libc::c_int>) -> Option<libc::c_int> {
101-
let fd = self.0.sync_init(
102-
|| match init() {
103-
// OK as val >= 0 and val <= c_int::MAX < usize::MAX
104-
Some(val) => val as usize,
105-
None => LazyUsize::UNINIT,
106-
},
107-
|| unsafe {
108-
// We are usually waiting on an open(2) syscall to complete,
109-
// which typically takes < 10us if the file is a device.
110-
// However, we might end up waiting much longer if the entropy
111-
// pool isn't initialized, but even in that case, this loop will
112-
// consume a negligible amount of CPU on most platforms.
113-
libc::usleep(10);
114-
},
115-
);
116-
match fd {
117-
LazyUsize::UNINIT => None,
118-
val => Some(val as libc::c_int),
119-
}
120-
}
121-
}
122-
123101
cfg_if! {
124102
if #[cfg(any(target_os = "linux", target_os = "emscripten"))] {
125103
use libc::open64 as open;
@@ -129,15 +107,15 @@ cfg_if! {
129107
}
130108

131109
// SAFETY: path must be null terminated, FD must be manually closed.
132-
pub unsafe fn open_readonly(path: &str) -> Option<libc::c_int> {
110+
pub unsafe fn open_readonly(path: &str) -> Result<libc::c_int, Error> {
133111
debug_assert!(path.as_bytes().last() == Some(&0));
134-
let fd = open(path.as_ptr() as *mut _, libc::O_RDONLY | libc::O_CLOEXEC);
112+
let fd = open(path.as_ptr() as *const _, libc::O_RDONLY | libc::O_CLOEXEC);
135113
if fd < 0 {
136-
return None;
114+
return Err(last_os_error());
137115
}
138116
// O_CLOEXEC works on all Unix targets except for older Linux kernels (pre
139117
// 2.6.23), so we also use an ioctl to make sure FD_CLOEXEC is set.
140118
#[cfg(target_os = "linux")]
141119
libc::ioctl(fd, libc::FIOCLEX);
142-
Some(fd)
120+
Ok(fd)
143121
}

0 commit comments

Comments
 (0)