forked from rust-random/getrandom
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlinux_android_with_fallback.rs
More file actions
83 lines (73 loc) · 3 KB
/
linux_android_with_fallback.rs
File metadata and controls
83 lines (73 loc) · 3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//! Implementation for Linux / Android with `/dev/urandom` fallback
use super::{sanitizer, use_file};
use crate::{Error, lazy, util_libc};
use core::{
ffi::c_void,
mem::{MaybeUninit, transmute},
ptr,
};
pub use crate::util::{inner_u32, inner_u64};
type GetRandomFn = unsafe extern "C" fn(*mut c_void, libc::size_t, libc::c_uint) -> libc::ssize_t;
/// Sentinel value which indicates that `libc::getrandom` either not available,
/// or not supported by kernel.
const NOT_AVAILABLE: *mut c_void = usize::MAX as *mut c_void;
#[cold]
#[inline(never)]
fn init() -> *mut c_void {
// Use static linking to `libc::getrandom` on MUSL targets and `dlsym` everywhere else
#[cfg(not(target_env = "musl"))]
let fptr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"getrandom".as_ptr()) };
#[cfg(target_env = "musl")]
let fptr = {
let fptr: GetRandomFn = libc::getrandom;
unsafe { transmute::<GetRandomFn, *mut c_void>(fptr) }
};
let res_ptr = if !fptr.is_null() {
let getrandom_fn = unsafe { transmute::<*mut c_void, GetRandomFn>(fptr) };
// Check that `getrandom` syscall is supported by kernel
let res = unsafe { getrandom_fn(ptr::dangling_mut(), 0, 0) };
if cfg!(getrandom_test_linux_fallback) {
NOT_AVAILABLE
} else if res.is_negative() {
match util_libc::last_os_error().raw_os_error() {
Some(libc::ENOSYS) => NOT_AVAILABLE, // No kernel support
// The fallback on EPERM is intentionally not done on Android since this workaround
// seems to be needed only for specific Linux-based products that aren't based
// on Android. See https://github.com/rust-random/getrandom/issues/229.
#[cfg(target_os = "linux")]
Some(libc::EPERM) => NOT_AVAILABLE, // Blocked by seccomp
_ => fptr,
}
} else {
fptr
}
} else {
NOT_AVAILABLE
};
#[cfg(getrandom_test_linux_without_fallback)]
if res_ptr == NOT_AVAILABLE {
panic!("Fallback is triggered with enabled `getrandom_test_linux_without_fallback`")
}
res_ptr
}
// Prevent inlining of the fallback implementation
#[inline(never)]
fn use_file_fallback(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
use_file::fill_inner(dest)
}
#[inline]
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
static GETRANDOM_FN: lazy::LazyPtr<c_void> = lazy::LazyPtr::new();
let fptr = GETRANDOM_FN.unsync_init(init);
if fptr == NOT_AVAILABLE {
use_file_fallback(dest)
} else {
// note: `transmute` is currently the only way to convert a pointer into a function reference
let getrandom_fn = unsafe { transmute::<*mut c_void, GetRandomFn>(fptr) };
util_libc::sys_fill_exact(dest, |buf| unsafe {
let ret = getrandom_fn(buf.as_mut_ptr().cast(), buf.len(), 0);
sanitizer::unpoison_linux_getrandom_result(buf, ret);
ret
})
}
}