diff --git a/redox-rt/src/proc.rs b/redox-rt/src/proc.rs index cb84a0688d73b3d480b4d7843cdd76bb07c58fca..edd90eb1c6f3b30bc9ec1dae279001e33481f4ad 100644 --- a/redox-rt/src/proc.rs +++ b/redox-rt/src/proc.rs @@ -733,7 +733,10 @@ pub fn fork_inner(initial_rsp: *mut usize) -> Result<usize> { let (cur_filetable_fd, new_pid_fd, new_pid); { - let cur_pid_fd = FdGuard::new(syscall::open("/scheme/thisproc/current/open_via_dup", O_CLOEXEC)?); + let cur_pid_fd = FdGuard::new(syscall::open( + "/scheme/thisproc/current/open_via_dup", + O_CLOEXEC, + )?); (new_pid_fd, new_pid) = new_child_process()?; copy_str(*cur_pid_fd, *new_pid_fd, "name")?; @@ -861,7 +864,10 @@ pub fn fork_inner(initial_rsp: *mut usize) -> Result<usize> { pub fn new_child_process() -> Result<(FdGuard, usize)> { // Create a new context (fields such as uid/gid will be inherited from the current context). - let fd = FdGuard::new(syscall::open("/scheme/thisproc/new/open_via_dup", O_CLOEXEC)?); + let fd = FdGuard::new(syscall::open( + "/scheme/thisproc/new/open_via_dup", + O_CLOEXEC, + )?); // Extract pid. let mut buffer = [0_u8; 64]; diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index 25255a8487e4da24d9e8cc5d80b9eeb309fca587..747525e2f05103671540989c7eb0834e8fa8d2d9 100644 --- a/src/header/stdlib/mod.rs +++ b/src/header/stdlib/mod.rs @@ -416,16 +416,15 @@ pub extern "C" fn grantpt(fildes: c_int) -> c_int { 0 } +// Ported from musl #[no_mangle] pub unsafe extern "C" fn initstate(seed: c_uint, state: *mut c_char, size: size_t) -> *mut c_char { - // Ported from musl - if size < 8 { ptr::null_mut() } else { - // TODO: lock? - let old_state = random::save_state(); - random::N = match size { + let mut random_state = random::state_lock(); + let old_state = random_state.save(); + random_state.n = match size { 0..=7 => unreachable!(), // ensured above 8..=31 => 0, 32..=63 => 7, @@ -434,10 +433,9 @@ pub unsafe extern "C" fn initstate(seed: c_uint, state: *mut c_char, size: size_ _ => 63, }; - random::X_PTR = (state.cast::<[u8; 4]>()).offset(1); - random::seed(seed); - random::save_state(); - // TODO: unlock? + random_state.x_ptr = (state.cast::<[u8; 4]>()).offset(1); + random_state.seed(seed); + random_state.save(); old_state.cast::<_>() } @@ -905,39 +903,39 @@ pub unsafe extern "C" fn rand_r(seed: *mut c_uint) -> c_int { } } +// Ported from musl #[no_mangle] pub unsafe extern "C" fn random() -> c_long { - // Ported from musl + let mut random_state = random::state_lock(); let k: u32; - // TODO: lock? - random::ensure_x_ptr_init(); - if random::N == 0 { - let x_old = u32::from_ne_bytes(*random::X_PTR); + random_state.ensure_x_ptr_init(); + + if random_state.n == 0 { + let x_old = u32::from_ne_bytes(*random_state.x_ptr); let x_new = random::lcg31_step(x_old); - *random::X_PTR = x_new.to_ne_bytes(); + *random_state.x_ptr = x_new.to_ne_bytes(); k = x_new; } else { // The non-u32-aligned way of saying x[i] += x[j]... - let x_i_old = u32::from_ne_bytes(*random::X_PTR.add(usize::from(random::I))); - let x_j = u32::from_ne_bytes(*random::X_PTR.add(usize::from(random::J))); + let x_i_old = u32::from_ne_bytes(*random_state.x_ptr.add(usize::from(random_state.i))); + let x_j = u32::from_ne_bytes(*random_state.x_ptr.add(usize::from(random_state.j))); let x_i_new = x_i_old.wrapping_add(x_j); - *random::X_PTR.add(usize::from(random::I)) = x_i_new.to_ne_bytes(); + *random_state.x_ptr.add(usize::from(random_state.i)) = x_i_new.to_ne_bytes(); k = x_i_new >> 1; - random::I += 1; - if random::I == random::N { - random::I = 0; + random_state.i += 1; + if random_state.i == random_state.n { + random_state.i = 0; } - random::J += 1; - if random::J == random::N { - random::J = 0; + random_state.j += 1; + if random_state.j == random_state.n { + random_state.j = 0; } } - // TODO: unlock? /* Both branches of this function result in a "u31", which will * always fit in a c_long. */ @@ -1064,14 +1062,13 @@ pub unsafe extern "C" fn setkey(key: *const c_char) { unimplemented!(); } +// Ported from musl. The state parameter is no longer const in newer versions of POSIX. #[no_mangle] pub unsafe extern "C" fn setstate(state: *mut c_char) -> *mut c_char { - /* Ported from musl. The state parameter is no longer const in newer - * versions of POSIX. */ + let mut random_state = random::state_lock(); - // TODO: lock? - let old_state = random::save_state(); - random::load_state(state.cast::<_>()); + let old_state = random_state.save(); + random_state.load(state.cast::<_>()); // TODO: unlock? old_state.cast::<_>() } @@ -1095,13 +1092,12 @@ pub extern "C" fn srand48(seedval: c_long) { .unwrap(); } +// Ported from musl #[no_mangle] pub unsafe extern "C" fn srandom(seed: c_uint) { - // Ported from musl + let mut random_state = random::state_lock(); - // TODO: lock? - random::seed(seed); - // TODO: unlock? + random_state.seed(seed); } #[no_mangle] diff --git a/src/header/stdlib/random.rs b/src/header/stdlib/random.rs index f08155924ea6993478352a69833d46f14e4b374b..85c22b2e661df75789b61b8c81335bc0163e46ba 100644 --- a/src/header/stdlib/random.rs +++ b/src/header/stdlib/random.rs @@ -1,97 +1,117 @@ //! Helper functions for random() and friends, see https://pubs.opengroup.org/onlinepubs/7908799/xsh/initstate.html -/* Ported from musl's implementation (src/prng/random.c). Does not - * currently implement locking, though. */ +// Ported from musl's implementation (src/prng/random.c) -use crate::platform::types::*; +use crate::{ + platform::types::*, + sync::{Mutex, MutexGuard}, +}; use core::{convert::TryFrom, ptr}; -#[rustfmt::skip] -static mut X_INIT: [u32; 32] = [ - 0x00000000, 0x5851f42d, 0xc0b18ccf, 0xcbb5f646, - 0xc7033129, 0x30705b04, 0x20fd5db4, 0x9a8b7f78, - 0x502959d8, 0xab894868, 0x6c0356a7, 0x88cdb7ff, - 0xb477d43f, 0x70a3a52b, 0xa8e4baf1, 0xfd8341fc, - 0x8ae16fd9, 0x742d2f7a, 0x0d1f0796, 0x76035e09, - 0x40f7702c, 0x6fa72ca5, 0xaaa84157, 0x58a0df74, - 0xc74a0364, 0xae533cc4, 0x04185faf, 0x6de3b115, - 0x0cab8628, 0xf043bfa4, 0x398150e9, 0x37521657, -]; - -/* N needs to accommodate values up to 63, corresponding to the maximum - * state array size of 256 bytes. I and J must be able to accommodate - * values less than or equal to N. */ -pub static mut N: u8 = 31; -pub static mut I: u8 = 3; -pub static mut J: u8 = 0; - -/* As such, random() and related functions work on u32 values, but POSIX - * allows the user to supply a custom state data array as a `char *` - * with no requirements on alignment. Thus, we must assume the worst in - * terms of alignment and convert back and forth from [u8; 4]. - * - * Also, unlike in C, we can't take the address of the initializing - * array outside of a function. */ -pub static mut X_PTR: *mut [u8; 4] = ptr::null_mut(); - -// To be called in any function that may read from X_PTR -pub unsafe fn ensure_x_ptr_init() { - if X_PTR.is_null() { - let x_u32_ptr: *mut u32 = &mut X_INIT[1]; - X_PTR = x_u32_ptr.cast::<[u8; 4]>(); - } +pub struct State { + pub x_init: [u32; 32], + pub x_ptr: *mut [u8; 4], + pub n: u8, + pub i: u8, + pub j: u8, } -pub fn lcg31_step(x: u32) -> u32 { - 1103515245_u32.wrapping_mul(x).wrapping_add(12345_u32) & 0x7fffffff -} +// Necessary because raw pointers are not Send +unsafe impl Send for State {} -pub fn lcg64_step(x: u64) -> u64 { - 6364136223846793005_u64.wrapping_mul(x).wrapping_add(1_u64) -} +impl State { + /// To be called in any function that may read from X_PTR + pub fn ensure_x_ptr_init(&mut self) { + if self.x_ptr.is_null() { + let x_u32_ptr: *mut u32 = &mut self.x_init[1]; + self.x_ptr = x_u32_ptr.cast::<[u8; 4]>(); + } + } -pub unsafe fn save_state() -> *mut [u8; 4] { - ensure_x_ptr_init(); + pub unsafe fn save(&mut self) -> *mut [u8; 4] { + self.ensure_x_ptr_init(); - let stash_value: u32 = (u32::from(N) << 16) | (u32::from(I) << 8) | u32::from(J); - *X_PTR.offset(-1) = stash_value.to_ne_bytes(); - X_PTR.offset(-1) -} + let stash_value: u32 = + (u32::from(self.n) << 16) | (u32::from(self.i) << 8) | u32::from(self.j); + *self.x_ptr.offset(-1) = stash_value.to_ne_bytes(); + self.x_ptr.offset(-1) + } -pub unsafe fn load_state(state_ptr: *mut [u8; 4]) { - let stash_value = u32::from_ne_bytes(*state_ptr); - X_PTR = state_ptr.offset(1); + pub unsafe fn load(&mut self, state_ptr: *mut [u8; 4]) { + let stash_value = u32::from_ne_bytes(*state_ptr); + self.x_ptr = state_ptr.offset(1); - /* This calculation of N does not have a bit mask in the musl - * original, in principle resulting in a u16, but obtaining a value - * larger than 63 can probably be dismissed as pathological. */ - N = u8::try_from((stash_value >> 16) & 0xff).unwrap(); + /* This calculation of n does not have a bit mask in the musl + * original, in principle resulting in a u16, but obtaining a value + * larger than 63 can probably be dismissed as pathological. */ + self.n = u8::try_from((stash_value >> 16) & 0xff).unwrap(); - // I and J calculations are straight from musl - I = u8::try_from((stash_value >> 8) & 0xff).unwrap(); - J = u8::try_from(stash_value & 0xff).unwrap(); -} + // i and j calculations are straight from musl + self.i = u8::try_from((stash_value >> 8) & 0xff).unwrap(); + self.j = u8::try_from(stash_value & 0xff).unwrap(); + } -pub unsafe fn seed(seed: c_uint) { - ensure_x_ptr_init(); + pub unsafe fn seed(&mut self, seed: c_uint) { + self.ensure_x_ptr_init(); - let mut s = seed as u64; + let mut s = seed as u64; - if N == 0 { - *X_PTR = (s as u32).to_ne_bytes(); - } else { - I = if N == 31 || N == 7 { 3 } else { 1 }; + if self.n == 0 { + *self.x_ptr = (s as u32).to_ne_bytes(); + } else { + self.i = if self.n == 31 || self.n == 7 { 3 } else { 1 }; - J = 0; + self.j = 0; - for k in 0..usize::from(N) { - s = lcg64_step(s); + for k in 0..usize::from(self.n) { + s = lcg64_step(s); - // Conversion will always succeed (value is a 32-bit right- - // shift of a 64-bit integer). - *X_PTR.add(k) = u32::try_from(s >> 32).unwrap().to_ne_bytes(); - } + // Conversion will always succeed (value is a 32-bit right- + // shift of a 64-bit integer). + *self.x_ptr.add(k) = u32::try_from(s >> 32).unwrap().to_ne_bytes(); + } - // ensure X contains at least one odd number - *X_PTR = (u32::from_ne_bytes(*X_PTR) | 1).to_ne_bytes(); + // ensure X contains at least one odd number + *self.x_ptr = (u32::from_ne_bytes(*self.x_ptr) | 1).to_ne_bytes(); + } } } + +pub fn state_lock<'a>() -> MutexGuard<'a, State> { + static STATE: Mutex<State> = Mutex::new(State { + #[rustfmt::skip] + x_init: [ + 0x00000000, 0x5851f42d, 0xc0b18ccf, 0xcbb5f646, + 0xc7033129, 0x30705b04, 0x20fd5db4, 0x9a8b7f78, + 0x502959d8, 0xab894868, 0x6c0356a7, 0x88cdb7ff, + 0xb477d43f, 0x70a3a52b, 0xa8e4baf1, 0xfd8341fc, + 0x8ae16fd9, 0x742d2f7a, 0x0d1f0796, 0x76035e09, + 0x40f7702c, 0x6fa72ca5, 0xaaa84157, 0x58a0df74, + 0xc74a0364, 0xae533cc4, 0x04185faf, 0x6de3b115, + 0x0cab8628, 0xf043bfa4, 0x398150e9, 0x37521657, + ], + /* As such, random() and related functions work on u32 values, but POSIX + * allows the user to supply a custom state data array as a `char *` + * with no requirements on alignment. Thus, we must assume the worst in + * terms of alignment and convert back and forth from [u8; 4]. + * + * Also, unlike in C, we can't take the address of the initializing + * array outside of a function. */ + x_ptr: ptr::null_mut(), + /* N needs to accommodate values up to 63, corresponding to the maximum + * state array size of 256 bytes. I and J must be able to accommodate + * values less than or equal to N. */ + n: 31, + i: 3, + j: 0, + }); + + STATE.try_lock().expect("unable to acquire PRNG lock") +} + +pub fn lcg31_step(x: u32) -> u32 { + 1103515245_u32.wrapping_mul(x).wrapping_add(12345_u32) & 0x7fffffff +} + +pub fn lcg64_step(x: u64) -> u64 { + 6364136223846793005_u64.wrapping_mul(x).wrapping_add(1_u64) +} diff --git a/src/platform/redox/exec.rs b/src/platform/redox/exec.rs index 81ac0de70f9ec5cfd1547aad0c8d2aa66fb3be4d..edc7304d37045e1eb0119f624cfaaf5d8bedb465 100644 --- a/src/platform/redox/exec.rs +++ b/src/platform/redox/exec.rs @@ -252,7 +252,8 @@ pub fn execve( // scenarios. While execve() is undefined according to POSIX if there exist sibling // threads, it could still be allowed by keeping certain file descriptors and instead // set the active file table. - let files_fd = File::new(syscall::open("/scheme/thisproc/current/filetable", O_RDONLY)? as c_int); + let files_fd = + File::new(syscall::open("/scheme/thisproc/current/filetable", O_RDONLY)? as c_int); for line in BufReader::new(files_fd).lines() { let line = match line { Ok(l) => l, diff --git a/src/platform/redox/ptrace.rs b/src/platform/redox/ptrace.rs index a5c4f84b8819e2bfff51940dfc7c9a14fe5fcc47..e35f24b7e1dc44df74ecd1ad57e2b8467649a086 100644 --- a/src/platform/redox/ptrace.rs +++ b/src/platform/redox/ptrace.rs @@ -82,11 +82,15 @@ pub fn get_session( NEW_FLAGS, )?, regs: File::open( - CStr::borrow(&CString::new(format!("/scheme/proc/{}/regs/int", pid)).unwrap()), + CStr::borrow( + &CString::new(format!("/scheme/proc/{}/regs/int", pid)).unwrap(), + ), NEW_FLAGS, )?, fpregs: File::open( - CStr::borrow(&CString::new(format!("/scheme/proc/{}/regs/float", pid)).unwrap()), + CStr::borrow( + &CString::new(format!("/scheme/proc/{}/regs/float", pid)).unwrap(), + ), NEW_FLAGS, )?, })) @@ -134,7 +138,9 @@ fn inner_ptrace( // Mark this child as traced, parent will check for this marker file let pid = Sys::getpid(); mem::forget(File::open( - CStr::borrow(&CString::new(format!("/scheme/chan/ptrace-relibc/{}/traceme", pid)).unwrap()), + CStr::borrow( + &CString::new(format!("/scheme/chan/ptrace-relibc/{}/traceme", pid)).unwrap(), + ), fcntl::O_CREAT | fcntl::O_PATH | fcntl::O_EXCL, )?); return Ok(0);