diff options
Diffstat (limited to 'library/std/src/sys')
42 files changed, 447 insertions, 604 deletions
diff --git a/library/std/src/sys/hermit/condvar.rs b/library/std/src/sys/hermit/condvar.rs deleted file mode 100644 index 22059ca0dbe..00000000000 --- a/library/std/src/sys/hermit/condvar.rs +++ /dev/null @@ -1,90 +0,0 @@ -use crate::ffi::c_void; -use crate::ptr; -use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; -use crate::sys::hermit::abi; -use crate::sys::locks::Mutex; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; -use crate::time::Duration; - -// The implementation is inspired by Andrew D. Birrell's paper -// "Implementing Condition Variables with Semaphores" - -pub struct Condvar { - counter: AtomicUsize, - sem1: *const c_void, - sem2: *const c_void, -} - -pub(crate) type MovableCondvar = LazyBox<Condvar>; - -impl LazyInit for Condvar { - fn init() -> Box<Self> { - Box::new(Self::new()) - } -} - -unsafe impl Send for Condvar {} -unsafe impl Sync for Condvar {} - -impl Condvar { - pub fn new() -> Self { - let mut condvar = - Self { counter: AtomicUsize::new(0), sem1: ptr::null(), sem2: ptr::null() }; - unsafe { - let _ = abi::sem_init(&mut condvar.sem1, 0); - let _ = abi::sem_init(&mut condvar.sem2, 0); - } - condvar - } - - pub unsafe fn notify_one(&self) { - if self.counter.load(SeqCst) > 0 { - self.counter.fetch_sub(1, SeqCst); - abi::sem_post(self.sem1); - abi::sem_timedwait(self.sem2, 0); - } - } - - pub unsafe fn notify_all(&self) { - let counter = self.counter.swap(0, SeqCst); - for _ in 0..counter { - abi::sem_post(self.sem1); - } - for _ in 0..counter { - abi::sem_timedwait(self.sem2, 0); - } - } - - pub unsafe fn wait(&self, mutex: &Mutex) { - self.counter.fetch_add(1, SeqCst); - mutex.unlock(); - abi::sem_timedwait(self.sem1, 0); - abi::sem_post(self.sem2); - mutex.lock(); - } - - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - self.counter.fetch_add(1, SeqCst); - mutex.unlock(); - let millis = dur.as_millis().min(u32::MAX as u128) as u32; - - let res = if millis > 0 { - abi::sem_timedwait(self.sem1, millis) - } else { - abi::sem_trywait(self.sem1) - }; - - abi::sem_post(self.sem2); - mutex.lock(); - res == 0 - } -} - -impl Drop for Condvar { - fn drop(&mut self) { - unsafe { - let _ = abi::sem_destroy(self.sem1); - let _ = abi::sem_destroy(self.sem2); - } - } -} diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index 1c5efa94bd3..f921839cf52 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -41,6 +41,9 @@ pub struct OpenOptions { mode: i32, } +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes {} + pub struct FilePermissions(!); pub struct FileType(!); @@ -110,6 +113,11 @@ impl fmt::Debug for FilePermissions { } } +impl FileTimes { + pub fn set_accessed(&mut self, _t: SystemTime) {} + pub fn set_modified(&mut self, _t: SystemTime) {} +} + impl FileType { pub fn is_dir(&self) -> bool { self.0 @@ -344,6 +352,10 @@ impl File { pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { Err(Error::from_raw_os_error(22)) } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) + } } impl DirBuilder { diff --git a/library/std/src/sys/hermit/futex.rs b/library/std/src/sys/hermit/futex.rs new file mode 100644 index 00000000000..b64c174b06c --- /dev/null +++ b/library/std/src/sys/hermit/futex.rs @@ -0,0 +1,39 @@ +use super::abi; +use crate::ptr::null; +use crate::sync::atomic::AtomicU32; +use crate::time::Duration; + +pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool { + // Calculate the timeout as a relative timespec. + // + // Overflows are rounded up to an infinite timeout (None). + let timespec = timeout.and_then(|dur| { + Some(abi::timespec { + tv_sec: dur.as_secs().try_into().ok()?, + tv_nsec: dur.subsec_nanos().into(), + }) + }); + + let r = unsafe { + abi::futex_wait( + futex.as_mut_ptr(), + expected, + timespec.as_ref().map_or(null(), |t| t as *const abi::timespec), + abi::FUTEX_RELATIVE_TIMEOUT, + ) + }; + + r != -abi::errno::ETIMEDOUT +} + +#[inline] +pub fn futex_wake(futex: &AtomicU32) -> bool { + unsafe { abi::futex_wake(futex.as_mut_ptr(), 1) > 0 } +} + +#[inline] +pub fn futex_wake_all(futex: &AtomicU32) { + unsafe { + abi::futex_wake(futex.as_mut_ptr(), i32::MAX); + } +} diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index 60b7a973cc2..827d82900ea 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -25,6 +25,7 @@ pub mod cmath; pub mod env; pub mod fd; pub mod fs; +pub mod futex; #[path = "../unsupported/io.rs"] pub mod io; pub mod memchr; @@ -45,14 +46,14 @@ pub mod thread_local_dtor; pub mod thread_local_key; pub mod time; -mod condvar; -mod mutex; -mod rwlock; - +#[path = "../unix/locks"] pub mod locks { - pub use super::condvar::*; - pub use super::mutex::*; - pub use super::rwlock::*; + mod futex_condvar; + mod futex_mutex; + mod futex_rwlock; + pub(crate) use futex_condvar::MovableCondvar; + pub(crate) use futex_mutex::{MovableMutex, Mutex}; + pub(crate) use futex_rwlock::{MovableRwLock, RwLock}; } use crate::io::ErrorKind; @@ -98,7 +99,7 @@ pub extern "C" fn __rust_abort() { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -pub unsafe fn init(argc: isize, argv: *const *const u8) { +pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { let _ = net::init(); args::init(argc, argv); } diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs deleted file mode 100644 index eb15a04ffcf..00000000000 --- a/library/std/src/sys/hermit/mutex.rs +++ /dev/null @@ -1,216 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::collections::VecDeque; -use crate::hint; -use crate::ops::{Deref, DerefMut, Drop}; -use crate::ptr; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sys::hermit::abi; - -/// This type provides a lock based on busy waiting to realize mutual exclusion -/// -/// # Description -/// -/// This structure behaves a lot like a common mutex. There are some differences: -/// -/// - By using busy waiting, it can be used outside the runtime. -/// - It is a so called ticket lock and is completely fair. -#[cfg_attr(target_arch = "x86_64", repr(align(128)))] -#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))] -struct Spinlock<T: ?Sized> { - queue: AtomicUsize, - dequeue: AtomicUsize, - data: UnsafeCell<T>, -} - -unsafe impl<T: ?Sized + Send> Sync for Spinlock<T> {} -unsafe impl<T: ?Sized + Send> Send for Spinlock<T> {} - -/// A guard to which the protected data can be accessed -/// -/// When the guard falls out of scope it will release the lock. -struct SpinlockGuard<'a, T: ?Sized + 'a> { - dequeue: &'a AtomicUsize, - data: &'a mut T, -} - -impl<T> Spinlock<T> { - pub const fn new(user_data: T) -> Spinlock<T> { - Spinlock { - queue: AtomicUsize::new(0), - dequeue: AtomicUsize::new(1), - data: UnsafeCell::new(user_data), - } - } - - #[inline] - fn obtain_lock(&self) { - let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1; - let mut counter: u16 = 0; - while self.dequeue.load(Ordering::SeqCst) != ticket { - counter += 1; - if counter < 100 { - hint::spin_loop(); - } else { - counter = 0; - unsafe { - abi::yield_now(); - } - } - } - } - - #[inline] - pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> { - self.obtain_lock(); - SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() } - } -} - -impl<T: ?Sized + Default> Default for Spinlock<T> { - fn default() -> Spinlock<T> { - Spinlock::new(Default::default()) - } -} - -impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> { - type Target = T; - fn deref(&self) -> &T { - &*self.data - } -} - -impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> { - fn deref_mut(&mut self) -> &mut T { - &mut *self.data - } -} - -impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> { - /// The dropping of the SpinlockGuard will release the lock it was created from. - fn drop(&mut self) { - self.dequeue.fetch_add(1, Ordering::SeqCst); - } -} - -/// Realize a priority queue for tasks -struct PriorityQueue { - queues: [Option<VecDeque<abi::Tid>>; abi::NO_PRIORITIES], - prio_bitmap: u64, -} - -impl PriorityQueue { - pub const fn new() -> PriorityQueue { - PriorityQueue { - queues: [ - None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, - ], - prio_bitmap: 0, - } - } - - /// Add a task id by its priority to the queue - pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) { - let i: usize = prio.into().into(); - self.prio_bitmap |= (1 << i) as u64; - if let Some(queue) = &mut self.queues[i] { - queue.push_back(id); - } else { - let mut queue = VecDeque::new(); - queue.push_back(id); - self.queues[i] = Some(queue); - } - } - - fn pop_from_queue(&mut self, queue_index: usize) -> Option<abi::Tid> { - if let Some(queue) = &mut self.queues[queue_index] { - let id = queue.pop_front(); - - if queue.is_empty() { - self.prio_bitmap &= !(1 << queue_index as u64); - } - - id - } else { - None - } - } - - /// Pop the task handle with the highest priority from the queue - pub fn pop(&mut self) -> Option<abi::Tid> { - for i in 0..abi::NO_PRIORITIES { - if self.prio_bitmap & (1 << i) != 0 { - return self.pop_from_queue(i); - } - } - - None - } -} - -struct MutexInner { - locked: bool, - blocked_task: PriorityQueue, -} - -impl MutexInner { - pub const fn new() -> MutexInner { - MutexInner { locked: false, blocked_task: PriorityQueue::new() } - } -} - -pub struct Mutex { - inner: Spinlock<MutexInner>, -} - -pub type MovableMutex = Mutex; - -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} - -impl Mutex { - pub const fn new() -> Mutex { - Mutex { inner: Spinlock::new(MutexInner::new()) } - } - - #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] - pub unsafe fn lock(&self) { - loop { - let mut guard = self.inner.lock(); - if guard.locked == false { - guard.locked = true; - return; - } else { - let prio = abi::get_priority(); - let id = abi::getpid(); - - guard.blocked_task.push(prio, id); - abi::block_current_task(); - drop(guard); - abi::yield_now(); - } - } - } - - #[inline] - pub unsafe fn unlock(&self) { - let mut guard = self.inner.lock(); - guard.locked = false; - if let Some(tid) = guard.blocked_task.pop() { - abi::wakeup_task(tid); - } - } - - #[inline] - pub unsafe fn try_lock(&self) -> bool { - let mut guard = self.inner.lock(); - if guard.locked == false { - guard.locked = true; - } - guard.locked - } -} diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs index 74547617150..8a13879d8cc 100644 --- a/library/std/src/sys/hermit/net.rs +++ b/library/std/src/sys/hermit/net.rs @@ -487,6 +487,4 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr {} - - pub type socklen_t = usize; } diff --git a/library/std/src/sys/hermit/rwlock.rs b/library/std/src/sys/hermit/rwlock.rs deleted file mode 100644 index 9701bab1f66..00000000000 --- a/library/std/src/sys/hermit/rwlock.rs +++ /dev/null @@ -1,144 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::sys::locks::{MovableCondvar, Mutex}; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; - -pub struct RwLock { - lock: Mutex, - cond: MovableCondvar, - state: UnsafeCell<State>, -} - -pub type MovableRwLock = RwLock; - -enum State { - Unlocked, - Reading(usize), - Writing, -} - -unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} - -// This rwlock implementation is a relatively simple implementation which has a -// condition variable for readers/writers as well as a mutex protecting the -// internal state of the lock. A current downside of the implementation is that -// unlocking the lock will notify *all* waiters rather than just readers or just -// writers. This can cause lots of "thundering stampede" problems. While -// hopefully correct this implementation is very likely to want to be changed in -// the future. - -impl RwLock { - pub const fn new() -> RwLock { - RwLock { - lock: Mutex::new(), - cond: MovableCondvar::new(), - state: UnsafeCell::new(State::Unlocked), - } - } - - #[inline] - pub unsafe fn read(&self) { - self.lock.lock(); - while !(*self.state.get()).inc_readers() { - self.cond.wait(&self.lock); - } - self.lock.unlock(); - } - - #[inline] - pub unsafe fn try_read(&self) -> bool { - self.lock.lock(); - let ok = (*self.state.get()).inc_readers(); - self.lock.unlock(); - return ok; - } - - #[inline] - pub unsafe fn write(&self) { - self.lock.lock(); - while !(*self.state.get()).inc_writers() { - self.cond.wait(&self.lock); - } - self.lock.unlock(); - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - self.lock.lock(); - let ok = (*self.state.get()).inc_writers(); - self.lock.unlock(); - return ok; - } - - #[inline] - pub unsafe fn read_unlock(&self) { - self.lock.lock(); - let notify = (*self.state.get()).dec_readers(); - self.lock.unlock(); - if notify { - // FIXME: should only wake up one of these some of the time - self.cond.notify_all(); - } - } - - #[inline] - pub unsafe fn write_unlock(&self) { - self.lock.lock(); - (*self.state.get()).dec_writers(); - self.lock.unlock(); - // FIXME: should only wake up one of these some of the time - self.cond.notify_all(); - } -} - -impl State { - fn inc_readers(&mut self) -> bool { - match *self { - State::Unlocked => { - *self = State::Reading(1); - true - } - State::Reading(ref mut cnt) => { - *cnt += 1; - true - } - State::Writing => false, - } - } - - fn inc_writers(&mut self) -> bool { - match *self { - State::Unlocked => { - *self = State::Writing; - true - } - State::Reading(_) | State::Writing => false, - } - } - - fn dec_readers(&mut self) -> bool { - let zero = match *self { - State::Reading(ref mut cnt) => { - *cnt -= 1; - *cnt == 0 - } - State::Unlocked | State::Writing => invalid(), - }; - if zero { - *self = State::Unlocked; - } - zero - } - - fn dec_writers(&mut self) { - match *self { - State::Writing => {} - State::Unlocked | State::Reading(_) => invalid(), - } - *self = State::Unlocked; - } -} - -fn invalid() -> ! { - panic!("inconsistent rwlock"); -} diff --git a/library/std/src/sys/itron/mutex.rs b/library/std/src/sys/itron/mutex.rs index 715e94c3b3d..085662e6d44 100644 --- a/library/std/src/sys/itron/mutex.rs +++ b/library/std/src/sys/itron/mutex.rs @@ -31,12 +31,6 @@ impl Mutex { Mutex { mtx: SpinIdOnceCell::new() } } - pub unsafe fn init(&mut self) { - // Initialize `self.mtx` eagerly - let id = new_mtx().unwrap_or_else(|e| fail(e, &"acre_mtx")); - unsafe { self.mtx.set_unchecked((id, ())) }; - } - /// Get the inner mutex's ID, which is lazily created. fn raw(&self) -> abi::ID { match self.mtx.get_or_try_init(|| new_mtx().map(|id| (id, ()))) { diff --git a/library/std/src/sys/sgx/abi/thread.rs b/library/std/src/sys/sgx/abi/thread.rs index ef55b821a2b..2b23e368cc3 100644 --- a/library/std/src/sys/sgx/abi/thread.rs +++ b/library/std/src/sys/sgx/abi/thread.rs @@ -7,7 +7,11 @@ use fortanix_sgx_abi::Tcs; #[unstable(feature = "sgx_platform", issue = "56975")] pub fn current() -> Tcs { extern "C" { - fn get_tcs_addr() -> Tcs; + fn get_tcs_addr() -> *mut u8; + } + let addr = unsafe { get_tcs_addr() }; + match Tcs::new(addr) { + Some(tcs) => tcs, + None => rtabort!("TCS must not be placed at address zero (this is a linker error)"), } - unsafe { get_tcs_addr() } } diff --git a/library/std/src/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs index 5409bd1777c..0d934318c22 100644 --- a/library/std/src/sys/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/sgx/abi/usercalls/alloc.rs @@ -316,9 +316,9 @@ where // | small1 | Chunk smaller than 8 bytes // +--------+ fn region_as_aligned_chunks(ptr: *const u8, len: usize) -> (usize, usize, usize) { - let small0_size = if ptr as usize % 8 == 0 { 0 } else { 8 - ptr as usize % 8 }; - let small1_size = (len - small0_size as usize) % 8; - let big_size = len - small0_size as usize - small1_size as usize; + let small0_size = if ptr.is_aligned_to(8) { 0 } else { 8 - ptr.addr() % 8 }; + let small1_size = (len - small0_size) % 8; + let big_size = len - small0_size - small1_size; (small0_size, big_size, small1_size) } @@ -364,8 +364,8 @@ pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize) mfence lfence ", - val = in(reg_byte) *src.offset(off as isize), - dst = in(reg) dst.offset(off as isize), + val = in(reg_byte) *src.add(off), + dst = in(reg) dst.add(off), seg_sel = in(reg) &mut seg_sel, options(nostack, att_syntax) ); @@ -378,8 +378,8 @@ pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize) assert!(is_enclave_range(src, len)); assert!(is_user_range(dst, len)); assert!(len < isize::MAX as usize); - assert!(!(src as usize).overflowing_add(len).1); - assert!(!(dst as usize).overflowing_add(len).1); + assert!(!src.addr().overflowing_add(len).1); + assert!(!dst.addr().overflowing_add(len).1); if len < 8 { // Can't align on 8 byte boundary: copy safely byte per byte @@ -404,17 +404,17 @@ pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize) unsafe { // Copy small0 - copy_bytewise_to_userspace(src, dst, small0_size as _); + copy_bytewise_to_userspace(src, dst, small0_size); // Copy big - let big_src = src.offset(small0_size as _); - let big_dst = dst.offset(small0_size as _); - copy_quadwords(big_src as _, big_dst, big_size); + let big_src = src.add(small0_size); + let big_dst = dst.add(small0_size); + copy_quadwords(big_src, big_dst, big_size); // Copy small1 - let small1_src = src.offset(big_size as isize + small0_size as isize); - let small1_dst = dst.offset(big_size as isize + small0_size as isize); - copy_bytewise_to_userspace(small1_src, small1_dst, small1_size as _); + let small1_src = src.add(big_size + small0_size); + let small1_dst = dst.add(big_size + small0_size); + copy_bytewise_to_userspace(small1_src, small1_dst, small1_size); } } } diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs index 696400670e0..b1d32929ecf 100644 --- a/library/std/src/sys/sgx/mod.rs +++ b/library/std/src/sys/sgx/mod.rs @@ -47,7 +47,7 @@ pub mod locks { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -pub unsafe fn init(argc: isize, argv: *const *const u8) { +pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { unsafe { args::init(argc, argv); } diff --git a/library/std/src/sys/sgx/mutex.rs b/library/std/src/sys/sgx/mutex.rs index 513cd77fd2a..aa747d56b0d 100644 --- a/library/std/src/sys/sgx/mutex.rs +++ b/library/std/src/sys/sgx/mutex.rs @@ -21,9 +21,6 @@ impl Mutex { } #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] pub unsafe fn lock(&self) { let mut guard = self.inner.lock(); if *guard.lock_var() { diff --git a/library/std/src/sys/solid/mod.rs b/library/std/src/sys/solid/mod.rs index 778a589d1b7..5867979a2a7 100644 --- a/library/std/src/sys/solid/mod.rs +++ b/library/std/src/sys/solid/mod.rs @@ -56,7 +56,7 @@ pub mod locks { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} +pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} // SAFETY: must be called only once during runtime cleanup. pub unsafe fn cleanup() {} diff --git a/library/std/src/sys/solid/os.rs b/library/std/src/sys/solid/os.rs index b5649d6e0ff..eecb347e599 100644 --- a/library/std/src/sys/solid/os.rs +++ b/library/std/src/sys/solid/os.rs @@ -8,7 +8,7 @@ use crate::os::{ solid::ffi::{OsStrExt, OsStringExt}, }; use crate::path::{self, PathBuf}; -use crate::sys_common::rwlock::StaticRwLock; +use crate::sync::RwLock; use crate::vec; use super::{error, itron, memchr}; @@ -78,7 +78,7 @@ pub fn current_exe() -> io::Result<PathBuf> { unsupported() } -static ENV_LOCK: StaticRwLock = StaticRwLock::new(); +static ENV_LOCK: RwLock<()> = RwLock::new(()); pub struct Env { iter: vec::IntoIter<(OsString, OsString)>, diff --git a/library/std/src/sys/unix/locks/fuchsia_mutex.rs b/library/std/src/sys/unix/locks/fuchsia_mutex.rs index ce427599c3b..117611ce43f 100644 --- a/library/std/src/sys/unix/locks/fuchsia_mutex.rs +++ b/library/std/src/sys/unix/locks/fuchsia_mutex.rs @@ -86,9 +86,6 @@ impl Mutex { } #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] pub unsafe fn try_lock(&self) -> bool { let thread_self = zx_thread_self(); self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed).is_ok() @@ -138,7 +135,7 @@ impl Mutex { } } - // The state has changed or a wakeup occured, try to lock the mutex. + // The state has changed or a wakeup occurred, try to lock the mutex. match self.futex.compare_exchange(UNLOCKED, owned_state, Acquire, Relaxed) { Ok(_) => return, Err(updated) => state = updated, diff --git a/library/std/src/sys/unix/locks/futex_mutex.rs b/library/std/src/sys/unix/locks/futex_mutex.rs index 99ba86e5f99..33b13dad4d6 100644 --- a/library/std/src/sys/unix/locks/futex_mutex.rs +++ b/library/std/src/sys/unix/locks/futex_mutex.rs @@ -20,9 +20,6 @@ impl Mutex { } #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] pub unsafe fn try_lock(&self) -> bool { self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_ok() } @@ -53,7 +50,7 @@ impl Mutex { // We avoid an unnecessary write if it as already set to 2, // to be friendlier for the caches. if state != 2 && self.futex.swap(2, Acquire) == 0 { - // We changed it from 0 to 2, so we just succesfully locked it. + // We changed it from 0 to 2, so we just successfully locked it. return; } diff --git a/library/std/src/sys/unix/locks/futex_rwlock.rs b/library/std/src/sys/unix/locks/futex_rwlock.rs index b3bbbf743f8..0cc92244eca 100644 --- a/library/std/src/sys/unix/locks/futex_rwlock.rs +++ b/library/std/src/sys/unix/locks/futex_rwlock.rs @@ -54,7 +54,7 @@ fn is_read_lockable(state: u32) -> bool { // We don't allow read-locking if there's readers waiting, even if the lock is unlocked // and there's no writers waiting. The only situation when this happens is after unlocking, // at which point the unlocking thread might be waking up writers, which have priority over readers. - // The unlocking thread will clear the readers waiting bit and wake up readers, if necssary. + // The unlocking thread will clear the readers waiting bit and wake up readers, if necessary. state & MASK < MAX_READERS && !has_readers_waiting(state) && !has_writers_waiting(state) } diff --git a/library/std/src/sys/unix/locks/mod.rs b/library/std/src/sys/unix/locks/mod.rs index f5f92f69358..9bb314b7010 100644 --- a/library/std/src/sys/unix/locks/mod.rs +++ b/library/std/src/sys/unix/locks/mod.rs @@ -11,21 +11,21 @@ cfg_if::cfg_if! { mod futex_rwlock; mod futex_condvar; pub(crate) use futex_mutex::{Mutex, MovableMutex}; - pub(crate) use futex_rwlock::{RwLock, MovableRwLock}; + pub(crate) use futex_rwlock::MovableRwLock; pub(crate) use futex_condvar::MovableCondvar; } else if #[cfg(target_os = "fuchsia")] { mod fuchsia_mutex; mod futex_rwlock; mod futex_condvar; pub(crate) use fuchsia_mutex::{Mutex, MovableMutex}; - pub(crate) use futex_rwlock::{RwLock, MovableRwLock}; + pub(crate) use futex_rwlock::MovableRwLock; pub(crate) use futex_condvar::MovableCondvar; } else { mod pthread_mutex; mod pthread_rwlock; mod pthread_condvar; pub(crate) use pthread_mutex::{Mutex, MovableMutex}; - pub(crate) use pthread_rwlock::{RwLock, MovableRwLock}; + pub(crate) use pthread_rwlock::MovableRwLock; pub(crate) use pthread_condvar::MovableCondvar; } } diff --git a/library/std/src/sys/unix/locks/pthread_mutex.rs b/library/std/src/sys/unix/locks/pthread_mutex.rs index 98afee69ba6..5964935ddb5 100644 --- a/library/std/src/sys/unix/locks/pthread_mutex.rs +++ b/library/std/src/sys/unix/locks/pthread_mutex.rs @@ -52,7 +52,7 @@ impl Mutex { Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } } #[inline] - pub unsafe fn init(&mut self) { + unsafe fn init(&mut self) { // Issue #33770 // // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 3a375093099..c84e292eac1 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -44,12 +44,13 @@ pub mod thread_parker; pub mod time; #[cfg(target_os = "espidf")] -pub fn init(argc: isize, argv: *const *const u8) {} +pub fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {} #[cfg(not(target_os = "espidf"))] // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -pub unsafe fn init(argc: isize, argv: *const *const u8) { +// See `fn init()` in `library/std/src/rt.rs` for docs on `sigpipe`. +pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // The standard streams might be closed on application startup. To prevent // std::io::{stdin, stdout,stderr} objects from using other unrelated file // resources opened later, we reopen standards streams when they are closed. @@ -61,8 +62,9 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) { // want! // // Hence, we set SIGPIPE to ignore when the program starts up in order - // to prevent this problem. - reset_sigpipe(); + // to prevent this problem. Add `#[unix_sigpipe = "..."]` above `fn main()` to + // alter this behavior. + reset_sigpipe(sigpipe); stack_overflow::init(); args::init(argc, argv); @@ -151,9 +153,31 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) { } } - unsafe fn reset_sigpipe() { + unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) { #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))] - rtassert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR); + { + // We don't want to add this as a public type to libstd, nor do we + // want to `include!` a file from the compiler (which would break + // Miri and xargo for example), so we choose to duplicate these + // constants from `compiler/rustc_session/src/config/sigpipe.rs`. + // See the other file for docs. NOTE: Make sure to keep them in + // sync! + mod sigpipe { + pub const INHERIT: u8 = 1; + pub const SIG_IGN: u8 = 2; + pub const SIG_DFL: u8 = 3; + } + + let handler = match sigpipe { + sigpipe::INHERIT => None, + sigpipe::SIG_IGN => Some(libc::SIG_IGN), + sigpipe::SIG_DFL => Some(libc::SIG_DFL), + _ => unreachable!(), + }; + if let Some(handler) = handler { + rtassert!(signal(libc::SIGPIPE, handler) != libc::SIG_ERR); + } + } } } diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 46545a0839f..7c70ddbd9b9 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -17,10 +17,10 @@ use crate::path::{self, PathBuf}; use crate::ptr; use crate::slice; use crate::str; +use crate::sync::{PoisonError, RwLock}; use crate::sys::cvt; use crate::sys::fd; use crate::sys::memchr; -use crate::sys_common::rwlock::{StaticRwLock, StaticRwLockReadGuard}; use crate::vec; #[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] @@ -125,7 +125,9 @@ pub fn error_string(errno: i32) -> String { } let p = p as *const _; - str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() + // We can't always expect a UTF-8 environment. When we don't get that luxury, + // it's better to give a low-quality error message than none at all. + String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into() } } @@ -501,10 +503,10 @@ pub unsafe fn environ() -> *mut *const *const c_char { ptr::addr_of_mut!(environ) } -static ENV_LOCK: StaticRwLock = StaticRwLock::new(); +static ENV_LOCK: RwLock<()> = RwLock::new(()); -pub fn env_read_lock() -> StaticRwLockReadGuard { - ENV_LOCK.read() +pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) } /// Returns a vector of (variable, value) byte-vector pairs for all the diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 26ae6281771..2ff8e600f7c 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -822,14 +822,14 @@ impl crate::os::linux::process::ChildExt for crate::process::Child { self.handle .pidfd .as_ref() - .ok_or_else(|| Error::new(ErrorKind::Other, "No pidfd was created.")) + .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created.")) } fn take_pidfd(&mut self) -> io::Result<PidFd> { self.handle .pidfd .take() - .ok_or_else(|| Error::new(ErrorKind::Other, "No pidfd was created.")) + .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created.")) } } diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 7db3065dee0..56bb71b5dcb 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -429,7 +429,7 @@ mod cgroups { Some(b"") => Cgroup::V2, Some(controllers) if from_utf8(controllers) - .is_ok_and(|c| c.split(",").any(|c| c == "cpu")) => + .is_ok_and(|c| c.split(',').any(|c| c == "cpu")) => { Cgroup::V1 } diff --git a/library/std/src/sys/unix/thread_parker/mod.rs b/library/std/src/sys/unix/thread_parker/mod.rs new file mode 100644 index 00000000000..e2453580dc7 --- /dev/null +++ b/library/std/src/sys/unix/thread_parker/mod.rs @@ -0,0 +1,21 @@ +//! Thread parking on systems without futex support. + +#![cfg(not(any( + target_os = "linux", + target_os = "android", + all(target_os = "emscripten", target_feature = "atomics"), + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "fuchsia", +)))] + +cfg_if::cfg_if! { + if #[cfg(target_os = "netbsd")] { + mod netbsd; + pub use netbsd::Parker; + } else { + mod pthread; + pub use pthread::Parker; + } +} diff --git a/library/std/src/sys/unix/thread_parker/netbsd.rs b/library/std/src/sys/unix/thread_parker/netbsd.rs new file mode 100644 index 00000000000..7657605b52f --- /dev/null +++ b/library/std/src/sys/unix/thread_parker/netbsd.rs @@ -0,0 +1,113 @@ +use crate::ffi::{c_int, c_void}; +use crate::pin::Pin; +use crate::ptr::{null, null_mut}; +use crate::sync::atomic::{ + AtomicU64, + Ordering::{Acquire, Relaxed, Release}, +}; +use crate::time::Duration; +use libc::{_lwp_self, clockid_t, lwpid_t, time_t, timespec, CLOCK_MONOTONIC}; + +extern "C" { + fn ___lwp_park60( + clock_id: clockid_t, + flags: c_int, + ts: *mut timespec, + unpark: lwpid_t, + hint: *const c_void, + unparkhint: *const c_void, + ) -> c_int; + fn _lwp_unpark(lwp: lwpid_t, hint: *const c_void) -> c_int; +} + +/// The thread is not parked and the token is not available. +/// +/// Zero cannot be a valid LWP id, since it is used as empty value for the unpark +/// argument in _lwp_park. +const EMPTY: u64 = 0; +/// The token is available. Do not park anymore. +const NOTIFIED: u64 = u64::MAX; + +pub struct Parker { + /// The parker state. Contains either one of the two state values above or the LWP + /// id of the parked thread. + state: AtomicU64, +} + +impl Parker { + pub unsafe fn new(parker: *mut Parker) { + parker.write(Parker { state: AtomicU64::new(EMPTY) }) + } + + // Does not actually need `unsafe` or `Pin`, but the pthread implementation does. + pub unsafe fn park(self: Pin<&Self>) { + // If the token has already been made available, we can skip + // a bit of work, so check for it here. + if self.state.load(Acquire) != NOTIFIED { + let parked = _lwp_self() as u64; + let hint = self.state.as_mut_ptr().cast(); + if self.state.compare_exchange(EMPTY, parked, Relaxed, Acquire).is_ok() { + // Loop to guard against spurious wakeups. + loop { + ___lwp_park60(0, 0, null_mut(), 0, hint, null()); + if self.state.load(Acquire) == NOTIFIED { + break; + } + } + } + } + + // At this point, the change to NOTIFIED has always been observed with acquire + // ordering, so we can just use a relaxed store here (instead of a swap). + self.state.store(EMPTY, Relaxed); + } + + // Does not actually need `unsafe` or `Pin`, but the pthread implementation does. + pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) { + if self.state.load(Acquire) != NOTIFIED { + let parked = _lwp_self() as u64; + let hint = self.state.as_mut_ptr().cast(); + let mut timeout = timespec { + // Saturate so that the operation will definitely time out + // (even if it is after the heat death of the universe). + tv_sec: dur.as_secs().try_into().ok().unwrap_or(time_t::MAX), + tv_nsec: dur.subsec_nanos().into(), + }; + + if self.state.compare_exchange(EMPTY, parked, Relaxed, Acquire).is_ok() { + // Timeout needs to be mutable since it is modified on NetBSD 9.0 and + // above. + ___lwp_park60(CLOCK_MONOTONIC, 0, &mut timeout, 0, hint, null()); + // Use a swap to get acquire ordering even if the token was set after + // the timeout occurred. + self.state.swap(EMPTY, Acquire); + return; + } + } + + self.state.store(EMPTY, Relaxed); + } + + // Does not actually need `Pin`, but the pthread implementation does. + pub fn unpark(self: Pin<&Self>) { + let state = self.state.swap(NOTIFIED, Release); + if !matches!(state, EMPTY | NOTIFIED) { + let lwp = state as lwpid_t; + let hint = self.state.as_mut_ptr().cast(); + + // If the parking thread terminated and did not actually park, this will + // probably return an error, which is OK. In the worst case, another + // thread has received the same LWP id. It will then receive a spurious + // wakeup, but those are allowable per the API contract. The same reasoning + // applies if a timeout occurred before this call, but the state was not + // yet reset. + + // SAFETY: + // The syscall has no invariants to hold. Only unsafe because it is an + // extern function. + unsafe { + _lwp_unpark(lwp, hint); + } + } + } +} diff --git a/library/std/src/sys/unix/thread_parker.rs b/library/std/src/sys/unix/thread_parker/pthread.rs index ca1a7138fde..3dfc0026ed1 100644 --- a/library/std/src/sys/unix/thread_parker.rs +++ b/library/std/src/sys/unix/thread_parker/pthread.rs @@ -1,15 +1,5 @@ //! Thread parking without `futex` using the `pthread` synchronization primitives. -#![cfg(not(any( - target_os = "linux", - target_os = "android", - all(target_os = "emscripten", target_feature = "atomics"), - target_os = "freebsd", - target_os = "openbsd", - target_os = "dragonfly", - target_os = "fuchsia", -)))] - use crate::cell::UnsafeCell; use crate::marker::PhantomPinned; use crate::pin::Pin; @@ -59,8 +49,8 @@ unsafe fn wait_timeout( target_os = "espidf" ))] let (now, dur) = { - use super::time::SystemTime; use crate::cmp::min; + use crate::sys::time::SystemTime; // OSX implementation of `pthread_cond_timedwait` is buggy // with super long durations. When duration is greater than @@ -85,7 +75,7 @@ unsafe fn wait_timeout( target_os = "espidf" )))] let (now, dur) = { - use super::time::Timespec; + use crate::sys::time::Timespec; (Timespec::now(libc::CLOCK_MONOTONIC), dur) }; diff --git a/library/std/src/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs index 4c9ade4a8c7..5cd9e57de19 100644 --- a/library/std/src/sys/unsupported/common.rs +++ b/library/std/src/sys/unsupported/common.rs @@ -6,7 +6,7 @@ pub mod memchr { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} +pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. diff --git a/library/std/src/sys/unsupported/locks/condvar.rs b/library/std/src/sys/unsupported/locks/condvar.rs index e703fd0d269..527a26a12bc 100644 --- a/library/std/src/sys/unsupported/locks/condvar.rs +++ b/library/std/src/sys/unsupported/locks/condvar.rs @@ -7,6 +7,7 @@ pub type MovableCondvar = Condvar; impl Condvar { #[inline] + #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] pub const fn new() -> Condvar { Condvar {} } diff --git a/library/std/src/sys/unsupported/locks/mod.rs b/library/std/src/sys/unsupported/locks/mod.rs index d412ff152a0..602a2d6231a 100644 --- a/library/std/src/sys/unsupported/locks/mod.rs +++ b/library/std/src/sys/unsupported/locks/mod.rs @@ -3,4 +3,4 @@ mod mutex; mod rwlock; pub use condvar::{Condvar, MovableCondvar}; pub use mutex::{MovableMutex, Mutex}; -pub use rwlock::{MovableRwLock, RwLock}; +pub use rwlock::MovableRwLock; diff --git a/library/std/src/sys/unsupported/locks/mutex.rs b/library/std/src/sys/unsupported/locks/mutex.rs index d7cb12e0cf9..87ea475c6e3 100644 --- a/library/std/src/sys/unsupported/locks/mutex.rs +++ b/library/std/src/sys/unsupported/locks/mutex.rs @@ -12,14 +12,12 @@ unsafe impl Sync for Mutex {} // no threads on this platform impl Mutex { #[inline] + #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] pub const fn new() -> Mutex { Mutex { locked: Cell::new(false) } } #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] pub unsafe fn lock(&self) { assert_eq!(self.locked.replace(true), false, "cannot recursively acquire mutex"); } diff --git a/library/std/src/sys/unsupported/locks/rwlock.rs b/library/std/src/sys/unsupported/locks/rwlock.rs index aca5fb7152c..5292691b955 100644 --- a/library/std/src/sys/unsupported/locks/rwlock.rs +++ b/library/std/src/sys/unsupported/locks/rwlock.rs @@ -12,6 +12,7 @@ unsafe impl Sync for RwLock {} // no threads on this platform impl RwLock { #[inline] + #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] pub const fn new() -> RwLock { RwLock { mode: Cell::new(0) } } diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs index 4159efe2a05..93838390bee 100644 --- a/library/std/src/sys/wasm/mod.rs +++ b/library/std/src/sys/wasm/mod.rs @@ -57,7 +57,7 @@ cfg_if::cfg_if! { mod futex_rwlock; pub(crate) use futex_condvar::{Condvar, MovableCondvar}; pub(crate) use futex_mutex::{Mutex, MovableMutex}; - pub(crate) use futex_rwlock::{RwLock, MovableRwLock}; + pub(crate) use futex_rwlock::MovableRwLock; } #[path = "atomics/futex.rs"] pub mod futex; diff --git a/library/std/src/sys/windows/alloc.rs b/library/std/src/sys/windows/alloc.rs index fe00c08aa6a..d53ea16005f 100644 --- a/library/std/src/sys/windows/alloc.rs +++ b/library/std/src/sys/windows/alloc.rs @@ -16,6 +16,7 @@ mod tests; // Flag to indicate that the memory returned by `HeapAlloc` should be zeroed. const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008; +#[link(name = "kernel32")] extern "system" { // Get a handle to the default heap of the current process, or null if the operation fails. // diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index c99c8efe436..c61a7e7d1e4 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -66,6 +66,7 @@ pub type LPSYSTEM_INFO = *mut SYSTEM_INFO; pub type LPWSABUF = *mut WSABUF; pub type LPWSAOVERLAPPED = *mut c_void; pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = *mut c_void; +pub type BCRYPT_ALG_HANDLE = LPVOID; pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE; pub type PLARGE_INTEGER = *mut c_longlong; @@ -285,6 +286,8 @@ pub fn nt_success(status: NTSTATUS) -> bool { status >= 0 } +// "RNG\0" +pub const BCRYPT_RNG_ALGORITHM: &[u16] = &[b'R' as u16, b'N' as u16, b'G' as u16, 0]; pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD = 0x00000002; #[repr(C)] @@ -455,6 +458,12 @@ pub enum FILE_INFO_BY_HANDLE_CLASS { } #[repr(C)] +pub struct FILE_ATTRIBUTE_TAG_INFO { + pub FileAttributes: DWORD, + pub ReparseTag: DWORD, +} + +#[repr(C)] pub struct FILE_DISPOSITION_INFO { pub DeleteFile: BOOLEAN, } @@ -808,10 +817,6 @@ if #[cfg(not(target_vendor = "uwp"))] { #[link(name = "advapi32")] extern "system" { - // Forbidden when targeting UWP - #[link_name = "SystemFunction036"] - pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN; - // Allowed but unused by UWP pub fn OpenProcessToken( ProcessHandle: HANDLE, @@ -1223,11 +1228,18 @@ extern "system" { // >= Vista / Server 2008 // https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom pub fn BCryptGenRandom( - hAlgorithm: LPVOID, + hAlgorithm: BCRYPT_ALG_HANDLE, pBuffer: *mut u8, cbBuffer: ULONG, dwFlags: ULONG, ) -> NTSTATUS; + pub fn BCryptOpenAlgorithmProvider( + phalgorithm: *mut BCRYPT_ALG_HANDLE, + pszAlgId: LPCWSTR, + pszimplementation: LPCWSTR, + dwflags: ULONG, + ) -> NTSTATUS; + pub fn BCryptCloseAlgorithmProvider(hAlgorithm: BCRYPT_ALG_HANDLE, dwFlags: ULONG) -> NTSTATUS; } // Functions that aren't available on every version of Windows that we support, diff --git a/library/std/src/sys/windows/cmath.rs b/library/std/src/sys/windows/cmath.rs index 1a5421facd0..43ab8c7ee65 100644 --- a/library/std/src/sys/windows/cmath.rs +++ b/library/std/src/sys/windows/cmath.rs @@ -44,7 +44,7 @@ mod shims { } // On 32-bit x86 MSVC these functions aren't defined, so we just define shims -// which promote everything fo f64, perform the calculation, and then demote +// which promote everything to f64, perform the calculation, and then demote // back to f32. While not precisely correct should be "correct enough" for now. #[cfg(all(target_env = "msvc", target_arch = "x86"))] mod shims { diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 0aa7c50ded1..155d0297e49 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -3,7 +3,7 @@ use crate::os::windows::prelude::*; use crate::ffi::OsString; use crate::fmt; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem; +use crate::mem::{self, MaybeUninit}; use crate::os::windows::io::{AsHandle, BorrowedHandle}; use crate::path::{Path, PathBuf}; use crate::ptr; @@ -326,9 +326,15 @@ impl File { cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?; let mut reparse_tag = 0; if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - let mut b = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); - if let Ok((_, buf)) = self.reparse_point(&mut b) { - reparse_tag = (*buf).ReparseTag; + let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed(); + cvt(c::GetFileInformationByHandleEx( + self.handle.as_raw_handle(), + c::FileAttributeTagInfo, + ptr::addr_of_mut!(attr_tag).cast(), + mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(), + ))?; + if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + reparse_tag = attr_tag.ReparseTag; } } Ok(FileAttr { @@ -389,9 +395,15 @@ impl File { attr.file_size = info.AllocationSize as u64; attr.number_of_links = Some(info.NumberOfLinks); if attr.file_type().is_reparse_point() { - let mut b = Align8([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); - if let Ok((_, buf)) = self.reparse_point(&mut b) { - attr.reparse_tag = (*buf).ReparseTag; + let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed(); + cvt(c::GetFileInformationByHandleEx( + self.handle.as_raw_handle(), + c::FileAttributeTagInfo, + ptr::addr_of_mut!(attr_tag).cast(), + mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(), + ))?; + if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + attr.reparse_tag = attr_tag.ReparseTag; } } Ok(attr) @@ -463,7 +475,7 @@ impl File { // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`. fn reparse_point( &self, - space: &mut Align8<[u8]>, + space: &mut Align8<[MaybeUninit<u8>]>, ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> { unsafe { let mut bytes = 0; @@ -488,7 +500,7 @@ impl File { } fn readlink(&self) -> io::Result<PathBuf> { - let mut space = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); + let mut space = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); let (_bytes, buf) = self.reparse_point(&mut space)?; unsafe { let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag { @@ -658,12 +670,16 @@ impl File { /// A buffer for holding directory entries. struct DirBuff { - buffer: Box<Align8<[u8; Self::BUFFER_SIZE]>>, + buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>, } impl DirBuff { const BUFFER_SIZE: usize = 1024; fn new() -> Self { - Self { buffer: Box::new(Align8([0u8; Self::BUFFER_SIZE])) } + Self { + // Safety: `Align8<[MaybeUninit<u8>; N]>` does not need + // initialization. + buffer: unsafe { Box::new_uninit().assume_init() }, + } } fn capacity(&self) -> usize { self.buffer.0.len() @@ -676,8 +692,8 @@ impl DirBuff { DirBuffIter::new(self) } } -impl AsRef<[u8]> for DirBuff { - fn as_ref(&self) -> &[u8] { +impl AsRef<[MaybeUninit<u8>]> for DirBuff { + fn as_ref(&self) -> &[MaybeUninit<u8>] { &self.buffer.0 } } @@ -686,7 +702,7 @@ impl AsRef<[u8]> for DirBuff { /// /// Currently only returns file names (UTF-16 encoded). struct DirBuffIter<'a> { - buffer: Option<&'a [u8]>, + buffer: Option<&'a [MaybeUninit<u8>]>, cursor: usize, } impl<'a> DirBuffIter<'a> { @@ -701,9 +717,13 @@ impl<'a> Iterator for DirBuffIter<'a> { let buffer = &self.buffer?[self.cursor..]; // Get the name and next entry from the buffer. - // SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the - // last field (the file name) is unsized. So an offset has to be - // used to get the file name slice. + // SAFETY: + // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last + // field (the file name) is unsized. So an offset has to be used to + // get the file name slice. + // - The OS has guaranteed initialization of the fields of + // `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least + // `FileNameLength` bytes) let (name, is_directory, next_entry) = unsafe { let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>(); // Guaranteed to be aligned in documentation for @@ -1349,7 +1369,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { let h = f.as_inner().as_raw_handle(); unsafe { - let mut data = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); + let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); let data_ptr = data.0.as_mut_ptr(); let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>(); let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>(); diff --git a/library/std/src/sys/windows/locks/mod.rs b/library/std/src/sys/windows/locks/mod.rs index d412ff152a0..602a2d6231a 100644 --- a/library/std/src/sys/windows/locks/mod.rs +++ b/library/std/src/sys/windows/locks/mod.rs @@ -3,4 +3,4 @@ mod mutex; mod rwlock; pub use condvar::{Condvar, MovableCondvar}; pub use mutex::{MovableMutex, Mutex}; -pub use rwlock::{MovableRwLock, RwLock}; +pub use rwlock::MovableRwLock; diff --git a/library/std/src/sys/windows/locks/mutex.rs b/library/std/src/sys/windows/locks/mutex.rs index f91e8f9f59a..91207f5f466 100644 --- a/library/std/src/sys/windows/locks/mutex.rs +++ b/library/std/src/sys/windows/locks/mutex.rs @@ -37,8 +37,6 @@ impl Mutex { pub const fn new() -> Mutex { Mutex { srwlock: UnsafeCell::new(c::SRWLOCK_INIT) } } - #[inline] - pub unsafe fn init(&mut self) {} #[inline] pub unsafe fn lock(&self) { diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index 340cae4066b..eab9b961279 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -48,7 +48,7 @@ cfg_if::cfg_if! { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -pub unsafe fn init(_argc: isize, _argv: *const *const u8) { +pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { stack_overflow::init(); // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs index a0f82207099..beeca1917a9 100644 --- a/library/std/src/sys/windows/path.rs +++ b/library/std/src/sys/windows/path.rs @@ -198,14 +198,7 @@ fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) { match path.bytes().iter().position(|&x| separator(x)) { Some(separator_start) => { - let mut separator_end = separator_start + 1; - - // a series of multiple separator characters is treated as a single separator, - // except in verbatim paths - while !verbatim && separator_end < path.len() && separator(path.bytes()[separator_end]) - { - separator_end += 1; - } + let separator_end = separator_start + 1; let component = &path.bytes()[..separator_start]; diff --git a/library/std/src/sys/windows/path/tests.rs b/library/std/src/sys/windows/path/tests.rs index 2f7ec433bf2..623c6236166 100644 --- a/library/std/src/sys/windows/path/tests.rs +++ b/library/std/src/sys/windows/path/tests.rs @@ -31,16 +31,6 @@ fn test_parse_next_component() { parse_next_component(OsStr::new(r"servershare"), false), (OsStr::new(r"servershare"), OsStr::new("")) ); - - assert_eq!( - parse_next_component(OsStr::new(r"server/\//\/\\\\/////\/share"), false), - (OsStr::new(r"server"), OsStr::new(r"share")) - ); - - assert_eq!( - parse_next_component(OsStr::new(r"server\\\\\\\\\\\\\\share"), true), - (OsStr::new(r"server"), OsStr::new(r"\\\\\\\\\\\\\share")) - ); } #[test] @@ -115,7 +105,7 @@ fn test_parse_prefix_verbatim_device() { assert_eq!(prefix, parse_prefix(r"\\?/C:\windows\system32\notepad.exe")); } -// See #93586 for more infomation. +// See #93586 for more information. #[test] fn test_windows_prefix_components() { use crate::path::Path; @@ -126,3 +116,22 @@ fn test_windows_prefix_components() { assert_eq!(drive.as_os_str(), OsStr::new("C:")); assert_eq!(components.as_path(), Path::new("")); } + +/// See #101358. +/// +/// Note that the exact behaviour here may change in the future. +/// In which case this test will need to adjusted. +#[test] +fn broken_unc_path() { + use crate::path::Component; + + let mut components = Path::new(r"\\foo\\bar\\").components(); + assert_eq!(components.next(), Some(Component::RootDir)); + assert_eq!(components.next(), Some(Component::Normal("foo".as_ref()))); + assert_eq!(components.next(), Some(Component::Normal("bar".as_ref()))); + + let mut components = Path::new("//foo//bar//").components(); + assert_eq!(components.next(), Some(Component::RootDir)); + assert_eq!(components.next(), Some(Component::Normal("foo".as_ref()))); + assert_eq!(components.next(), Some(Component::Normal("bar".as_ref()))); +} diff --git a/library/std/src/sys/windows/rand.rs b/library/std/src/sys/windows/rand.rs index f8fd93a7398..b5a49489d3f 100644 --- a/library/std/src/sys/windows/rand.rs +++ b/library/std/src/sys/windows/rand.rs @@ -1,35 +1,106 @@ -use crate::io; +//! # Random key generation +//! +//! This module wraps the RNG provided by the OS. There are a few different +//! ways to interface with the OS RNG so it's worth exploring each of the options. +//! Note that at the time of writing these all go through the (undocumented) +//! `bcryptPrimitives.dll` but they use different route to get there. +//! +//! Originally we were using [`RtlGenRandom`], however that function is +//! deprecated and warns it "may be altered or unavailable in subsequent versions". +//! +//! So we switched to [`BCryptGenRandom`] with the `BCRYPT_USE_SYSTEM_PREFERRED_RNG` +//! flag to query and find the system configured RNG. However, this change caused a small +//! but significant number of users to experience panics caused by a failure of +//! this function. See [#94098]. +//! +//! The current version falls back to using `BCryptOpenAlgorithmProvider` if +//! `BCRYPT_USE_SYSTEM_PREFERRED_RNG` fails for any reason. +//! +//! [#94098]: https://github.com/rust-lang/rust/issues/94098 +//! [`RtlGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom +//! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom use crate::mem; use crate::ptr; use crate::sys::c; +/// Generates high quality secure random keys for use by [`HashMap`]. +/// +/// This is used to seed the default [`RandomState`]. +/// +/// [`HashMap`]: crate::collections::HashMap +/// [`RandomState`]: crate::collections::hash_map::RandomState pub fn hashmap_random_keys() -> (u64, u64) { - let mut v = (0, 0); - let ret = unsafe { - c::BCryptGenRandom( - ptr::null_mut(), - &mut v as *mut _ as *mut u8, - mem::size_of_val(&v) as c::ULONG, - c::BCRYPT_USE_SYSTEM_PREFERRED_RNG, - ) - }; - if ret != 0 { fallback_rng() } else { v } + Rng::SYSTEM.gen_random_keys().unwrap_or_else(fallback_rng) } -/// Generate random numbers using the fallback RNG function (RtlGenRandom) -#[cfg(not(target_vendor = "uwp"))] -#[inline(never)] -fn fallback_rng() -> (u64, u64) { - let mut v = (0, 0); - let ret = - unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut u8, mem::size_of_val(&v) as c::ULONG) }; +struct Rng { + algorithm: c::BCRYPT_ALG_HANDLE, + flags: u32, +} +impl Rng { + const SYSTEM: Self = unsafe { Self::new(ptr::null_mut(), c::BCRYPT_USE_SYSTEM_PREFERRED_RNG) }; + + /// Create the RNG from an existing algorithm handle. + /// + /// # Safety + /// + /// The handle must either be null or a valid algorithm handle. + const unsafe fn new(algorithm: c::BCRYPT_ALG_HANDLE, flags: u32) -> Self { + Self { algorithm, flags } + } + + /// Open a handle to the RNG algorithm. + fn open() -> Result<Self, c::NTSTATUS> { + use crate::sync::atomic::AtomicPtr; + use crate::sync::atomic::Ordering::{Acquire, Release}; + + // An atomic is used so we don't need to reopen the handle every time. + static HANDLE: AtomicPtr<crate::ffi::c_void> = AtomicPtr::new(ptr::null_mut()); + + let mut handle = HANDLE.load(Acquire); + if handle.is_null() { + let status = unsafe { + c::BCryptOpenAlgorithmProvider( + &mut handle, + c::BCRYPT_RNG_ALGORITHM.as_ptr(), + ptr::null(), + 0, + ) + }; + if c::nt_success(status) { + // If another thread opens a handle first then use that handle instead. + let result = HANDLE.compare_exchange(ptr::null_mut(), handle, Release, Acquire); + if let Err(previous_handle) = result { + // Close our handle and return the previous one. + unsafe { c::BCryptCloseAlgorithmProvider(handle, 0) }; + handle = previous_handle; + } + Ok(unsafe { Self::new(handle, 0) }) + } else { + Err(status) + } + } else { + Ok(unsafe { Self::new(handle, 0) }) + } + } - if ret != 0 { v } else { panic!("fallback RNG broken: {}", io::Error::last_os_error()) } + fn gen_random_keys(self) -> Result<(u64, u64), c::NTSTATUS> { + let mut v = (0, 0); + let status = unsafe { + let size = mem::size_of_val(&v).try_into().unwrap(); + c::BCryptGenRandom(self.algorithm, ptr::addr_of_mut!(v).cast(), size, self.flags) + }; + if c::nt_success(status) { Ok(v) } else { Err(status) } + } } -/// We can't use RtlGenRandom with UWP, so there is no fallback -#[cfg(target_vendor = "uwp")] +/// Generate random numbers using the fallback RNG function #[inline(never)] -fn fallback_rng() -> (u64, u64) { - panic!("fallback RNG broken: RtlGenRandom() not supported on UWP"); +fn fallback_rng(rng_status: c::NTSTATUS) -> (u64, u64) { + match Rng::open().and_then(|rng| rng.gen_random_keys()) { + Ok(keys) => keys, + Err(status) => { + panic!("RNG broken: {rng_status:#x}, fallback RNG broken: {status:#x}") + } + } } |
