Thanks for using Compiler Explorer
Sponsors
Jakt
C++
Ada
Analysis
Android Java
Android Kotlin
Assembly
C
C3
Carbon
C++ (Circle)
CIRCT
Clean
CMake
CMakeScript
COBOL
C++ for OpenCL
MLIR
Cppx
Cppx-Blue
Cppx-Gold
Cpp2-cppfront
Crystal
C#
CUDA C++
D
Dart
Elixir
Erlang
Fortran
F#
GLSL
Go
Haskell
HLSL
Hook
Hylo
IL
ispc
Java
Julia
Kotlin
LLVM IR
LLVM MIR
Modula-2
Nim
Objective-C
Objective-C++
OCaml
OpenCL C
Pascal
Pony
Python
Racket
Ruby
Rust
Snowball
Scala
Solidity
Spice
Swift
LLVM TableGen
Toit
TypeScript Native
V
Vala
Visual Basic
WASM
Zig
Javascript
GIMPLE
rust source #1
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
mrustc (master)
rustc 1.0.0
rustc 1.1.0
rustc 1.10.0
rustc 1.11.0
rustc 1.12.0
rustc 1.13.0
rustc 1.14.0
rustc 1.15.1
rustc 1.16.0
rustc 1.17.0
rustc 1.18.0
rustc 1.19.0
rustc 1.2.0
rustc 1.20.0
rustc 1.21.0
rustc 1.22.0
rustc 1.23.0
rustc 1.24.0
rustc 1.25.0
rustc 1.26.0
rustc 1.27.0
rustc 1.27.1
rustc 1.28.0
rustc 1.29.0
rustc 1.3.0
rustc 1.30.0
rustc 1.31.0
rustc 1.32.0
rustc 1.33.0
rustc 1.34.0
rustc 1.35.0
rustc 1.36.0
rustc 1.37.0
rustc 1.38.0
rustc 1.39.0
rustc 1.4.0
rustc 1.40.0
rustc 1.41.0
rustc 1.42.0
rustc 1.43.0
rustc 1.44.0
rustc 1.45.0
rustc 1.45.2
rustc 1.46.0
rustc 1.47.0
rustc 1.48.0
rustc 1.49.0
rustc 1.5.0
rustc 1.50.0
rustc 1.51.0
rustc 1.52.0
rustc 1.53.0
rustc 1.54.0
rustc 1.55.0
rustc 1.56.0
rustc 1.57.0
rustc 1.58.0
rustc 1.59.0
rustc 1.6.0
rustc 1.60.0
rustc 1.61.0
rustc 1.62.0
rustc 1.63.0
rustc 1.64.0
rustc 1.65.0
rustc 1.66.0
rustc 1.67.0
rustc 1.68.0
rustc 1.69.0
rustc 1.7.0
rustc 1.70.0
rustc 1.71.0
rustc 1.72.0
rustc 1.73.0
rustc 1.74.0
rustc 1.75.0
rustc 1.76.0
rustc 1.77.0
rustc 1.78.0
rustc 1.79.0
rustc 1.8.0
rustc 1.80.0
rustc 1.81.0
rustc 1.9.0
rustc beta
rustc nightly
rustc-cg-gcc (master)
x86-64 GCCRS (GCC master)
x86-64 GCCRS (GCCRS master)
x86-64 GCCRS 14.1 (GCC)
x86-64 GCCRS 14.2 (GCC)
Options
Source code
use core::{ cell::UnsafeCell, mem::MaybeUninit, num::NonZeroU32, sync::atomic::{AtomicUsize, Ordering::Relaxed}, }; use util_libc::{open_readonly, sys_fill_exact}; const FILE_PATH: &[u8] = b"/dev/urandom\0"; const FD_UNINIT: usize = usize::max_value(); static FD: AtomicUsize = AtomicUsize::new(FD_UNINIT); pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { let fd = get_rng_fd()?; sys_fill_exact(dest, |buf| unsafe { libc::read(fd, buf.as_mut_ptr().cast::<libc::c_void>(), buf.len()) }) } fn get_rng_fd() -> Result<libc::c_int, Error> { fn get_fd() -> Option<libc::c_int> { match FD.load(Relaxed) { FD_UNINIT => None, val => Some(val as libc::c_int), } } // Use double-checked locking to avoid acquiring the lock if possible. if let Some(fd) = get_fd() { return Ok(fd); } // SAFETY: We use the mutex only in this method, and we always unlock it // before returning, making sure we don't violate the pthread_mutex_t API. static MUTEX: Mutex = Mutex::new(); unsafe { MUTEX.lock() }; let _guard = DropGuard(|| unsafe { MUTEX.unlock() }); if let Some(fd) = get_fd() { return Ok(fd); } // // On Linux, /dev/urandom might return insecure values. wait_until_rng_ready()?; let fd = open_readonly(FILE_PATH)?; // The fd always fits in a usize without conflicting with FD_UNINIT. debug_assert!(fd >= 0 && (fd as usize) < FD_UNINIT); FD.store(fd as usize, Relaxed); Ok(fd) } // Succeeds once /dev/urandom is safe to read from fn wait_until_rng_ready() -> Result<(), Error> { // Poll /dev/random to make sure it is ok to read from /dev/urandom. let fd = open_readonly(b"/dev/random\0")?; let mut pfd = libc::pollfd { fd, events: libc::POLLIN, revents: 0, }; let _guard = DropGuard(|| unsafe { libc::close(fd); }); loop { // A negative timeout means an infinite timeout. let res = unsafe { libc::poll(&mut pfd, 1, -1) }; if res >= 0 { debug_assert_eq!(res, 1); // We only used one fd, and cannot timeout. return Ok(()); } let err = crate::util_libc::last_os_error(); match err.raw_os_error() { Some(libc::EINTR) | Some(libc::EAGAIN) => continue, _ => return Err(err), } } } #[derive(Copy, Clone, Eq, PartialEq)] pub struct Error(NonZeroU32); const fn internal_error(n: u16) -> Error { // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32. let code = Error::INTERNAL_START + (n as u32); Error(unsafe { NonZeroU32::new_unchecked(code) }) } impl Error { const ERRNO_NOT_POSITIVE: Error = internal_error(1); const UNEXPECTED: Error = internal_error(2); const INTERNAL_START: u32 = 1 << 31; fn raw_os_error(self) -> Option<i32> { if self.0.get() < Self::INTERNAL_START { Some(self.0.get() as i32) } else { None } } } impl From<NonZeroU32> for Error { fn from(code: NonZeroU32) -> Self { Self(code) } } struct Mutex(UnsafeCell<libc::pthread_mutex_t>); impl Mutex { const fn new() -> Self { Self(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER)) } unsafe fn lock(&self) { let r = libc::pthread_mutex_lock(self.0.get()); debug_assert_eq!(r, 0); } unsafe fn unlock(&self) { let r = libc::pthread_mutex_unlock(self.0.get()); debug_assert_eq!(r, 0); } } unsafe impl Sync for Mutex {} struct DropGuard<F: FnMut()>(F); impl<F: FnMut()> Drop for DropGuard<F> { fn drop(&mut self) { self.0() } } mod libc { #![allow(non_camel_case_types, dead_code)] pub use core::ffi::*; type nfds_t = c_ulong; type size_t = usize; pub type ssize_t = isize; #[repr(C)] pub struct pollfd { pub fd: c_int, pub events: c_short, pub revents: c_short, } #[repr(C)] pub struct pthread_mutex_t { size: [u8; __SIZEOF_PTHREAD_MUTEX_T], } const __SIZEOF_PTHREAD_MUTEX_T: usize = 40; pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { size: [0; __SIZEOF_PTHREAD_MUTEX_T], }; pub const POLLIN: c_short = 0x1; pub const EINTR: c_int = 4; pub const EAGAIN: c_int = 11; pub const O_RDONLY: c_int = 0; pub const O_CLOEXEC: c_int = 0x80000; #[inline(never)] #[no_mangle] pub unsafe extern "C" fn close(fd: c_int) -> c_int { 0 } #[inline(never)] #[no_mangle] pub unsafe extern "C" fn pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> c_int { 0 } extern "C" { pub fn read(fd: c_int, buf: *mut c_void, count: size_t) -> ssize_t; pub fn open(path: *const c_char, oflag: c_int) -> c_int; // pub fn close(fd: c_int) -> c_int; pub fn poll(fds: *mut pollfd, nfds: nfds_t, timeout: c_int) -> c_int; pub fn pthread_mutex_lock(lock: *mut pthread_mutex_t) -> c_int; // pub fn pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> c_int; pub fn __errno_location() -> *mut c_int; } } mod util_libc { use super::*; use libc::__errno_location as errno_location; unsafe fn get_errno() -> libc::c_int { *errno_location() } pub fn last_os_error() -> Error { let errno = unsafe { get_errno() }; if errno > 0 { Error::from(NonZeroU32::new(errno as u32).unwrap()) } else { Error::ERRNO_NOT_POSITIVE } } #[inline(always)] pub fn open_readonly(path: &[u8]) -> Result<libc::c_int, Error> { assert!(path.iter().any(|&b| b == 0)); loop { let fd = unsafe { libc::open( path.as_ptr().cast::<libc::c_char>(), libc::O_RDONLY | libc::O_CLOEXEC, ) }; if fd >= 0 { return Ok(fd); } let err = last_os_error(); // We should try again if open() was interrupted. if err.raw_os_error() != Some(libc::EINTR) { return Err(err); } } } pub fn sys_fill_exact( mut buf: &mut [MaybeUninit<u8>], sys_fill: impl Fn(&mut [MaybeUninit<u8>]) -> libc::ssize_t, ) -> Result<(), Error> { while !buf.is_empty() { let res = sys_fill(buf); match res { res if res > 0 => buf = buf.get_mut(res as usize..).ok_or(Error::UNEXPECTED)?, -1 => { let err = last_os_error(); // We should try again if the call was interrupted. if err.raw_os_error() != Some(libc::EINTR) { return Err(err); } } // Negative return codes not equal to -1 should be impossible. // EOF (ret = 0) should be impossible, as the data we are reading // should be an infinite stream of random bytes. _ => return Err(Error::UNEXPECTED), } } Ok(()) } }
Become a Patron
Sponsor on GitHub
Donate via PayPal
Source on GitHub
Mailing list
Installed libraries
Wiki
Report an issue
How it works
Contact the author
CE on Mastodon
About the author
Statistics
Changelog
Version tree