| 1 | //! Implementation for Linux / Android with `/dev/urandom` fallback | 
| 2 | use super::use_file; | 
|---|
| 3 | use crate::Error; | 
|---|
| 4 | use core::{ | 
|---|
| 5 | ffi::c_void, | 
|---|
| 6 | mem::{self, MaybeUninit}, | 
|---|
| 7 | ptr::{self, NonNull}, | 
|---|
| 8 | sync::atomic::{AtomicPtr, Ordering}, | 
|---|
| 9 | }; | 
|---|
| 10 | use use_file::util_libc; | 
|---|
| 11 |  | 
|---|
| 12 | pub use crate::util::{inner_u32, inner_u64}; | 
|---|
| 13 |  | 
|---|
| 14 | type GetRandomFn = unsafe extern "C"fn(*mut c_void, libc::size_t, libc::c_uint) -> libc::ssize_t; | 
|---|
| 15 |  | 
|---|
| 16 | /// Sentinel value which indicates that `libc::getrandom` either not available, | 
|---|
| 17 | /// or not supported by kernel. | 
|---|
| 18 | const NOT_AVAILABLE: NonNull<c_void> = unsafe { NonNull::new_unchecked(ptr:usize::MAX as *mut c_void) }; | 
|---|
| 19 |  | 
|---|
| 20 | static GETRANDOM_FN: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut()); | 
|---|
| 21 |  | 
|---|
| 22 | #[ cold] | 
|---|
| 23 | fn init() -> NonNull<c_void> { | 
|---|
| 24 | static NAME: &[u8] = b"getrandom\0 "; | 
|---|
| 25 | let name_ptr = NAME.as_ptr().cast::<libc::c_char>(); | 
|---|
| 26 | let raw_ptr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name_ptr) }; | 
|---|
| 27 | let res_ptr = match NonNull::new(raw_ptr) { | 
|---|
| 28 | Some(fptr) => { | 
|---|
| 29 | let getrandom_fn = unsafe { mem::transmute::<NonNull<c_void>, GetRandomFn>(fptr) }; | 
|---|
| 30 | let dangling_ptr = ptr::NonNull::dangling().as_ptr(); | 
|---|
| 31 | // Check that `getrandom` syscall is supported by kernel | 
|---|
| 32 | let res = unsafe { getrandom_fn(dangling_ptr, 0, 0) }; | 
|---|
| 33 | if cfg!(getrandom_test_linux_fallback) { | 
|---|
| 34 | NOT_AVAILABLE | 
|---|
| 35 | } else if res.is_negative() { | 
|---|
| 36 | match util_libc::last_os_error().raw_os_error() { | 
|---|
| 37 | Some(libc::ENOSYS) => NOT_AVAILABLE, // No kernel support | 
|---|
| 38 | // The fallback on EPERM is intentionally not done on Android since this workaround | 
|---|
| 39 | // seems to be needed only for specific Linux-based products that aren't based | 
|---|
| 40 | // on Android. See https://github.com/rust-random/getrandom/issues/229. | 
|---|
| 41 | #[ cfg(target_os = "linux")] | 
|---|
| 42 | Some(libc::EPERM) => NOT_AVAILABLE, // Blocked by seccomp | 
|---|
| 43 | _ => fptr, | 
|---|
| 44 | } | 
|---|
| 45 | } else { | 
|---|
| 46 | fptr | 
|---|
| 47 | } | 
|---|
| 48 | } | 
|---|
| 49 | None => NOT_AVAILABLE, | 
|---|
| 50 | }; | 
|---|
| 51 |  | 
|---|
| 52 | GETRANDOM_FN.store(res_ptr.as_ptr(), Ordering::Release); | 
|---|
| 53 | res_ptr | 
|---|
| 54 | } | 
|---|
| 55 |  | 
|---|
| 56 | // prevent inlining of the fallback implementation | 
|---|
| 57 | #[ inline(never)] | 
|---|
| 58 | fn use_file_fallback(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { | 
|---|
| 59 | use_file::fill_inner(dest) | 
|---|
| 60 | } | 
|---|
| 61 |  | 
|---|
| 62 | pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { | 
|---|
| 63 | // Despite being only a single atomic variable, we still cannot always use | 
|---|
| 64 | // Ordering::Relaxed, as we need to make sure a successful call to `init` | 
|---|
| 65 | // is "ordered before" any data read through the returned pointer (which | 
|---|
| 66 | // occurs when the function is called). Our implementation mirrors that of | 
|---|
| 67 | // the one in libstd, meaning that the use of non-Relaxed operations is | 
|---|
| 68 | // probably unnecessary. | 
|---|
| 69 | let raw_ptr: *mut c_void = GETRANDOM_FN.load(order:Ordering::Acquire); | 
|---|
| 70 | let fptr: NonNull = match NonNull::new(raw_ptr) { | 
|---|
| 71 | Some(p: NonNull) => p, | 
|---|
| 72 | None => init(), | 
|---|
| 73 | }; | 
|---|
| 74 |  | 
|---|
| 75 | if fptr == NOT_AVAILABLE { | 
|---|
| 76 | use_file_fallback(dest) | 
|---|
| 77 | } else { | 
|---|
| 78 | // note: `transume` is currently the only way to convert pointer into function reference | 
|---|
| 79 | let getrandom_fn: unsafe fn(*mut c_void, usize, …) -> … = unsafe { mem::transmute::<NonNull<c_void>, GetRandomFn>(src:fptr) }; | 
|---|
| 80 | util_libc::sys_fill_exact(buf:dest, |buf: &mut [MaybeUninit]| unsafe { | 
|---|
| 81 | getrandom_fn(buf.as_mut_ptr().cast(), buf.len(), 0) | 
|---|
| 82 | }) | 
|---|
| 83 | } | 
|---|
| 84 | } | 
|---|
| 85 |  | 
|---|