Skip to content

Commit 57f7bc5

Browse files
authored
Merge pull request #730 from jasonbking/illumos
637 Solaris getrandom() support breaks illumos
2 parents 731f3d7 + d80f5c1 commit 57f7bc5

File tree

2 files changed

+54
-35
lines changed

2 files changed

+54
-35
lines changed

rand_os/src/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,8 @@ trait OsRngImpl where Self: Sized {
283283
#[cfg(any(target_os = "linux", target_os = "android",
284284
target_os = "netbsd", target_os = "dragonfly",
285285
target_os = "solaris", target_os = "redox",
286-
target_os = "haiku", target_os = "emscripten"))]
286+
target_os = "haiku", target_os = "emscripten",
287+
target_os = "illumos"))]
287288
mod random_device;
288289

289290
macro_rules! mod_use {
@@ -309,7 +310,7 @@ mod_use!(cfg(target_os = "macos"), macos);
309310
mod_use!(cfg(target_os = "netbsd"), netbsd);
310311
mod_use!(cfg(target_os = "openbsd"), openbsd_bitrig);
311312
mod_use!(cfg(target_os = "redox"), redox);
312-
mod_use!(cfg(target_os = "solaris"), solaris);
313+
mod_use!(cfg(any(target_os = "solaris", target_os = "illumos")), solarish);
313314
mod_use!(cfg(windows), windows);
314315
mod_use!(cfg(target_env = "sgx"), sgx);
315316

@@ -376,6 +377,7 @@ mod imp {
376377
target_os = "openbsd",
377378
target_os = "redox",
378379
target_os = "solaris",
380+
target_os = "illumos",
379381
windows,
380382
target_arch = "wasm32",
381383
target_env = "sgx"

rand_os/src/solaris.rs renamed to rand_os/src/solarish.rs

+50-33
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ use std::io;
2828
use std::io::Read;
2929
use std::fs::{File, OpenOptions};
3030
use std::os::unix::fs::OpenOptionsExt;
31-
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
31+
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering, AtomicUsize};
3232
use std::cmp;
33+
use std::mem;
3334

3435
#[derive(Clone, Debug)]
3536
pub struct OsRng {
@@ -97,9 +98,10 @@ impl OsRngImpl for OsRng {
9798
}
9899

99100
fn max_chunk_size(&self) -> usize {
100-
// The documentation says 1024 is the maximum for getrandom, but
101-
// 1040 for /dev/random.
102-
1024
101+
// This is the largest size that's guaranteed to not block across
102+
// all the Solarish platforms, though some may allow for larger
103+
// sizes.
104+
256
103105
}
104106

105107
fn method_str(&self) -> &'static str {
@@ -110,18 +112,48 @@ impl OsRngImpl for OsRng {
110112
}
111113
}
112114

113-
fn getrandom(buf: &mut [u8], blocking: bool) -> libc::c_long {
114-
extern "C" {
115-
fn syscall(number: libc::c_long, ...) -> libc::c_long;
115+
#[cfg(target_os = "illumos")]
116+
type GetRandomFn = unsafe extern fn(*mut u8, libc::size_t, libc::c_uint)
117+
-> libc::ssize_t;
118+
#[cfg(target_os = "solaris")]
119+
type GetRandomFn = unsafe extern fn(*mut u8, libc::size_t, libc::c_uint)
120+
-> libc::c_int;
121+
122+
// Use dlsym to determine if getrandom(2) is present in libc. On Solarish
123+
// systems, the syscall interface is not stable and can change between
124+
// updates. Even worse, issuing unsupported syscalls will cause the system
125+
// to generate a SIGSYS signal (usually terminating the program).
126+
// Instead the stable APIs are exposed via libc. Cache the result of the
127+
// lookup for future calls. This is loosely modeled after the
128+
// libstd::sys::unix::weak macro which unfortunately is not exported.
129+
fn fetch() -> Option<GetRandomFn> {
130+
static FPTR: AtomicUsize = AtomicUsize::new(1);
131+
132+
if FPTR.load(Ordering::SeqCst) == 1 {
133+
let name = "getrandom\0";
134+
let addr = unsafe {
135+
libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) as usize
136+
};
137+
FPTR.store(addr, Ordering::SeqCst);
116138
}
117139

118-
const SYS_GETRANDOM: libc::c_long = 143;
140+
let ptr = FPTR.load(Ordering::SeqCst);
141+
unsafe {
142+
mem::transmute::<usize, Option<GetRandomFn>>(ptr)
143+
}
144+
}
145+
146+
fn getrandom(buf: &mut [u8], blocking: bool) -> libc::ssize_t {
119147
const GRND_NONBLOCK: libc::c_uint = 0x0001;
120148
const GRND_RANDOM: libc::c_uint = 0x0002;
121149

122-
unsafe {
123-
syscall(SYS_GETRANDOM, buf.as_mut_ptr(), buf.len(),
124-
if blocking { 0 } else { GRND_NONBLOCK } | GRND_RANDOM)
150+
if let Some(rand) = fetch() {
151+
let flag = if blocking { 0 } else { GRND_NONBLOCK } | GRND_RANDOM;
152+
unsafe {
153+
rand(buf.as_mut_ptr(), buf.len(), flag) as libc::ssize_t
154+
}
155+
} else {
156+
-1
125157
}
126158
}
127159

@@ -143,33 +175,18 @@ fn getrandom_try_fill(dest: &mut [u8], blocking: bool) -> Result<(), Error> {
143175
err,
144176
));
145177
}
146-
} else if result != dest.len() as i64 {
178+
} else if result != dest.len() as libc::ssize_t {
147179
return Err(Error::new(ErrorKind::Unavailable,
148180
"unexpected getrandom error"));
149181
}
150182
Ok(())
151183
}
152184

153185
fn is_getrandom_available() -> bool {
154-
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
155-
use std::sync::{Once, ONCE_INIT};
156-
157-
static CHECKER: Once = ONCE_INIT;
158-
static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;
159-
160-
CHECKER.call_once(|| {
161-
debug!("OsRng: testing getrandom");
162-
let mut buf: [u8; 0] = [];
163-
let result = getrandom(&mut buf, false);
164-
let available = if result == -1 {
165-
let err = io::Error::last_os_error().raw_os_error();
166-
err != Some(libc::ENOSYS)
167-
} else {
168-
true
169-
};
170-
AVAILABLE.store(available, Ordering::Relaxed);
171-
info!("OsRng: using {}", if available { "getrandom" } else { "/dev/random" });
172-
});
173-
174-
AVAILABLE.load(Ordering::Relaxed)
186+
let available = match fetch() {
187+
Some(_) => true,
188+
None => false,
189+
};
190+
info!("OsRng: using {}", if available { "getrandom" } else { "/dev/random" });
191+
available
175192
}

0 commit comments

Comments
 (0)