diff options
Diffstat (limited to 'library/std/src')
221 files changed, 2777 insertions, 1477 deletions
diff --git a/library/std/src/ascii.rs b/library/std/src/ascii.rs index 3a2880fd509..3813f3237fb 100644 --- a/library/std/src/ascii.rs +++ b/library/std/src/ascii.rs @@ -16,7 +16,7 @@ #[unstable(feature = "ascii_char", issue = "110998")] pub use core::ascii::Char; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::ascii::{escape_default, EscapeDefault}; +pub use core::ascii::{EscapeDefault, escape_default}; /// Extension methods for ASCII-subset only operations. /// diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index 7df9a8a14b0..fc333d7ff3f 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -91,9 +91,9 @@ mod tests; use crate::backtrace_rs::{self, BytesOrWideString}; use crate::ffi::c_void; use crate::panic::UnwindSafe; +use crate::sync::LazyLock; use crate::sync::atomic::AtomicU8; use crate::sync::atomic::Ordering::Relaxed; -use crate::sync::LazyLock; use crate::sys::backtrace::{lock, output_filename, set_image_base}; use crate::{env, fmt}; diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 822fa5791e3..1a18721b15e 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1037,6 +1037,7 @@ where /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_contains_key")] pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool where K: Borrow<Q>, @@ -1100,6 +1101,7 @@ where #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("push", "append", "put")] + #[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_insert")] pub fn insert(&mut self, k: K, v: V) -> Option<V> { self.base.insert(k, v) } @@ -1391,6 +1393,7 @@ where /// let iter = map.iter(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_iter_ty")] pub struct Iter<'a, K: 'a, V: 'a> { base: base::Iter<'a, K, V>, } @@ -1429,6 +1432,7 @@ impl<K: Debug, V: Debug> fmt::Debug for Iter<'_, K, V> { /// let iter = map.iter_mut(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_iter_mut_ty")] pub struct IterMut<'a, K: 'a, V: 'a> { base: base::IterMut<'a, K, V>, } @@ -1489,6 +1493,7 @@ impl<K, V> IntoIter<K, V> { /// let iter_keys = map.keys(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_keys_ty")] pub struct Keys<'a, K: 'a, V: 'a> { inner: Iter<'a, K, V>, } @@ -1527,6 +1532,7 @@ impl<K: Debug, V> fmt::Debug for Keys<'_, K, V> { /// let iter_values = map.values(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_values_ty")] pub struct Values<'a, K: 'a, V: 'a> { inner: Iter<'a, K, V>, } @@ -1565,6 +1571,7 @@ impl<K, V: Debug> fmt::Debug for Values<'_, K, V> { /// let iter = map.drain(); /// ``` #[stable(feature = "drain", since = "1.6.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_drain_ty")] pub struct Drain<'a, K: 'a, V: 'a> { base: base::Drain<'a, K, V>, } @@ -1622,6 +1629,7 @@ where /// let iter_values = map.values_mut(); /// ``` #[stable(feature = "map_values_mut", since = "1.10.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_values_mut_ty")] pub struct ValuesMut<'a, K: 'a, V: 'a> { inner: IterMut<'a, K, V>, } @@ -2754,7 +2762,6 @@ impl<'a, K, V> Entry<'a, K, V> { /// # Examples /// /// ``` - /// #![feature(entry_insert)] /// use std::collections::HashMap; /// /// let mut map: HashMap<&str, String> = HashMap::new(); @@ -2763,7 +2770,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// assert_eq!(entry.key(), &"poneyland"); /// ``` #[inline] - #[unstable(feature = "entry_insert", issue = "65225")] + #[stable(feature = "entry_insert", since = "CURRENT_RUSTC_VERSION")] pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { match self { Occupied(mut entry) => { @@ -3097,7 +3104,6 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { /// # Examples /// /// ``` - /// #![feature(entry_insert)] /// use std::collections::HashMap; /// use std::collections::hash_map::Entry; /// @@ -3109,7 +3115,7 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { /// assert_eq!(map["poneyland"], 37); /// ``` #[inline] - #[unstable(feature = "entry_insert", issue = "65225")] + #[stable(feature = "entry_insert", since = "CURRENT_RUSTC_VERSION")] pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { let base = self.base.insert_entry(value); OccupiedEntry { base } diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index 6641197c372..c28dd7b6b50 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -947,7 +947,7 @@ fn test_raw_entry() { mod test_extract_if { use super::*; - use crate::panic::{catch_unwind, AssertUnwindSafe}; + use crate::panic::{AssertUnwindSafe, catch_unwind}; use crate::sync::atomic::{AtomicUsize, Ordering}; trait EqSorted: Iterator { diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index d611353b0d3..4a113ddea3a 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -187,6 +187,7 @@ impl<T, S> HashSet<T, S> { #[inline] #[rustc_lint_query_instability] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "hashset_iter")] pub fn iter(&self) -> Iter<'_, T> { Iter { base: self.base.iter() } } @@ -1270,6 +1271,7 @@ where /// let mut iter = a.iter(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashset_iter_ty")] pub struct Iter<'a, K: 'a> { base: base::Iter<'a, K>, } @@ -1312,6 +1314,7 @@ pub struct IntoIter<K> { /// let mut drain = a.drain(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashset_drain_ty")] pub struct Drain<'a, K: 'a> { base: base::Drain<'a, K>, } diff --git a/library/std/src/collections/hash/set/tests.rs b/library/std/src/collections/hash/set/tests.rs index 4e635165272..7aa2167e213 100644 --- a/library/std/src/collections/hash/set/tests.rs +++ b/library/std/src/collections/hash/set/tests.rs @@ -1,8 +1,8 @@ use super::HashSet; use crate::hash::RandomState; -use crate::panic::{catch_unwind, AssertUnwindSafe}; -use crate::sync::atomic::{AtomicU32, Ordering}; +use crate::panic::{AssertUnwindSafe, catch_unwind}; use crate::sync::Arc; +use crate::sync::atomic::{AtomicU32, Ordering}; #[test] fn test_zero_capacities() { diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs index 3b04412e766..889ed3c5380 100644 --- a/library/std/src/collections/mod.rs +++ b/library/std/src/collections/mod.rs @@ -79,41 +79,49 @@ //! see each type's documentation, and note that the names of actual methods may //! differ from the tables below on certain collections. //! -//! Throughout the documentation, we will follow a few conventions. For all -//! operations, the collection's size is denoted by n. If another collection is -//! involved in the operation, it contains m elements. Operations which have an -//! *amortized* cost are suffixed with a `*`. Operations with an *expected* -//! cost are suffixed with a `~`. +//! Throughout the documentation, we will adhere to the following conventions +//! for operation notation: //! -//! All amortized costs are for the potential need to resize when capacity is -//! exhausted. If a resize occurs it will take *O*(*n*) time. Our collections never -//! automatically shrink, so removal operations aren't amortized. Over a -//! sufficiently large series of operations, the average cost per operation will -//! deterministically equal the given cost. +//! * The collection's size is denoted by `n`. +//! * If a second collection is involved, its size is denoted by `m`. +//! * Item indices are denoted by `i`. +//! * Operations which have an *amortized* cost are suffixed with a `*`. +//! * Operations with an *expected* cost are suffixed with a `~`. //! -//! Only [`HashMap`] has expected costs, due to the probabilistic nature of hashing. -//! It is theoretically possible, though very unlikely, for [`HashMap`] to -//! experience worse performance. +//! Calling operations that add to a collection will occasionally require a +//! collection to be resized - an extra operation that takes *O*(*n*) time. //! -//! ## Sequences +//! *Amortized* costs are calculated to account for the time cost of such resize +//! operations *over a sufficiently large series of operations*. An individual +//! operation may be slower or faster due to the sporadic nature of collection +//! resizing, however the average cost per operation will approach the amortized +//! cost. //! -//! | | get(i) | insert(i) | remove(i) | append | split_off(i) | -//! |----------------|------------------------|-------------------------|------------------------|-----------|------------------------| -//! | [`Vec`] | *O*(1) | *O*(*n*-*i*)* | *O*(*n*-*i*) | *O*(*m*)* | *O*(*n*-*i*) | -//! | [`VecDeque`] | *O*(1) | *O*(min(*i*, *n*-*i*))* | *O*(min(*i*, *n*-*i*)) | *O*(*m*)* | *O*(min(*i*, *n*-*i*)) | -//! | [`LinkedList`] | *O*(min(*i*, *n*-*i*)) | *O*(min(*i*, *n*-*i*)) | *O*(min(*i*, *n*-*i*)) | *O*(1) | *O*(min(*i*, *n*-*i*)) | +//! Rust's collections never automatically shrink, so removal operations aren't +//! amortized. //! -//! Note that where ties occur, [`Vec`] is generally going to be faster than [`VecDeque`], and -//! [`VecDeque`] is generally going to be faster than [`LinkedList`]. +//! [`HashMap`] uses *expected* costs. It is theoretically possible, though very +//! unlikely, for [`HashMap`] to experience significantly worse performance than +//! the expected cost. This is due to the probabilistic nature of hashing - i.e. +//! it is possible to generate a duplicate hash given some input key that will +//! requires extra computation to correct. //! -//! ## Maps +//! ## Cost of Collection Operations //! -//! For Sets, all operations have the cost of the equivalent Map operation. //! -//! | | get | insert | remove | range | append | -//! |--------------|---------------|---------------|---------------|---------------|--------------| -//! | [`HashMap`] | *O*(1)~ | *O*(1)~* | *O*(1)~ | N/A | N/A | -//! | [`BTreeMap`] | *O*(log(*n*)) | *O*(log(*n*)) | *O*(log(*n*)) | *O*(log(*n*)) | *O*(*n*+*m*) | +//! | | get(i) | insert(i) | remove(i) | append(Vec(m)) | split_off(i) | range | append | +//! |----------------|------------------------|-------------------------|------------------------|-------------------|------------------------|-----------------|--------------| +//! | [`Vec`] | *O*(1) | *O*(*n*-*i*)* | *O*(*n*-*i*) | *O*(*m*)* | *O*(*n*-*i*) | N/A | N/A | +//! | [`VecDeque`] | *O*(1) | *O*(min(*i*, *n*-*i*))* | *O*(min(*i*, *n*-*i*)) | *O*(*m*)* | *O*(min(*i*, *n*-*i*)) | N/A | N/A | +//! | [`LinkedList`] | *O*(min(*i*, *n*-*i*)) | *O*(min(*i*, *n*-*i*)) | *O*(min(*i*, *n*-*i*)) | *O*(1) | *O*(min(*i*, *n*-*i*)) | N/A | N/A | +//! | [`HashMap`] | *O*(1)~ | *O*(1)~* | *O*(1)~ | N/A | N/A | N/A | N/A | +//! | [`BTreeMap`] | *O*(log(*n*)) | *O*(log(*n*)) | *O*(log(*n*)) | N/A | N/A | *O*(log(*n*)) | *O*(*n*+*m*) | +//! +//! Note that where ties occur, [`Vec`] is generally going to be faster than +//! [`VecDeque`], and [`VecDeque`] is generally going to be faster than +//! [`LinkedList`]. +//! +//! For Sets, all operations have the cost of the equivalent Map operation. //! //! # Correct and Efficient Usage of Collections //! @@ -410,13 +418,13 @@ pub use alloc_crate::collections::TryReserveError; )] pub use alloc_crate::collections::TryReserveErrorKind; #[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::collections::{binary_heap, btree_map, btree_set}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::collections::{linked_list, vec_deque}; -#[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::collections::{BTreeMap, BTreeSet, BinaryHeap}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::collections::{LinkedList, VecDeque}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::collections::{binary_heap, btree_map, btree_set}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::collections::{linked_list, vec_deque}; #[stable(feature = "rust1", since = "1.0.0")] #[doc(inline)] diff --git a/library/std/src/env.rs b/library/std/src/env.rs index e06a851658c..97a1b846a91 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -354,12 +354,8 @@ impl Error for VarError { /// } /// assert_eq!(env::var(key), Ok("VALUE".to_string())); /// ``` -#[cfg_attr(bootstrap, rustc_deprecated_safe_2024)] -#[cfg_attr( - not(bootstrap), - rustc_deprecated_safe_2024( - audit_that = "the environment access only happens in single-threaded code" - ) +#[rustc_deprecated_safe_2024( + audit_that = "the environment access only happens in single-threaded code" )] #[stable(feature = "env", since = "1.0.0")] pub unsafe fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) { @@ -424,12 +420,8 @@ pub unsafe fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) { /// } /// assert!(env::var(key).is_err()); /// ``` -#[cfg_attr(bootstrap, rustc_deprecated_safe_2024)] -#[cfg_attr( - not(bootstrap), - rustc_deprecated_safe_2024( - audit_that = "the environment access only happens in single-threaded code" - ) +#[rustc_deprecated_safe_2024( + audit_that = "the environment access only happens in single-threaded code" )] #[stable(feature = "env", since = "1.0.0")] pub unsafe fn remove_var<K: AsRef<OsStr>>(key: K) { @@ -943,106 +935,147 @@ impl fmt::Debug for ArgsOs { pub mod consts { use crate::sys::env::os; - /// A string describing the architecture of the CPU that is currently - /// in use. + /// A string describing the architecture of the CPU that is currently in use. + /// An example value may be: `"x86"`, `"arm"` or `"riscv64"`. /// - /// Some possible values: + /// <details><summary>Full list of possible values</summary> /// - /// - x86 - /// - x86_64 - /// - arm - /// - aarch64 - /// - loongarch64 - /// - m68k - /// - csky - /// - mips - /// - mips64 - /// - powerpc - /// - powerpc64 - /// - riscv64 - /// - s390x - /// - sparc64 + /// * `"x86"` + /// * `"x86_64"` + /// * `"arm"` + /// * `"aarch64"` + /// * `"m68k"` + /// * `"mips"` + /// * `"mips32r6"` + /// * `"mips64"` + /// * `"mips64r6"` + /// * `"csky"` + /// * `"powerpc"` + /// * `"powerpc64"` + /// * `"riscv32"` + /// * `"riscv64"` + /// * `"s390x"` + /// * `"sparc"` + /// * `"sparc64"` + /// * `"hexagon"` + /// * `"loongarch64"` + /// + /// </details> #[stable(feature = "env", since = "1.0.0")] pub const ARCH: &str = env!("STD_ENV_ARCH"); - /// The family of the operating system. Example value is `unix`. + /// A string describing the family of the operating system. + /// An example value may be: `"unix"`, or `"windows"`. + /// + /// This value may be an empty string if the family is unknown. + /// + /// <details><summary>Full list of possible values</summary> /// - /// Some possible values: + /// * `"unix"` + /// * `"windows"` + /// * `"itron"` + /// * `"wasm"` + /// * `""` /// - /// - unix - /// - windows + /// </details> #[stable(feature = "env", since = "1.0.0")] pub const FAMILY: &str = os::FAMILY; /// A string describing the specific operating system in use. - /// Example value is `linux`. + /// An example value may be: `"linux"`, or `"freebsd"`. /// - /// Some possible values: + /// <details><summary>Full list of possible values</summary> /// - /// - linux - /// - macos - /// - ios - /// - freebsd - /// - dragonfly - /// - netbsd - /// - openbsd - /// - solaris - /// - android - /// - windows + /// * `"linux"` + /// * `"windows"` + /// * `"macos"` + /// * `"android"` + /// * `"ios"` + /// * `"openbsd"` + /// * `"freebsd"` + /// * `"netbsd"` + /// * `"wasi"` + /// * `"hermit"` + /// * `"aix"` + /// * `"apple"` + /// * `"dragonfly"` + /// * `"emscripten"` + /// * `"espidf"` + /// * `"fortanix"` + /// * `"uefi"` + /// * `"fuchsia"` + /// * `"haiku"` + /// * `"hermit"` + /// * `"watchos"` + /// * `"visionos"` + /// * `"tvos"` + /// * `"horizon"` + /// * `"hurd"` + /// * `"illumos"` + /// * `"l4re"` + /// * `"nto"` + /// * `"redox"` + /// * `"solaris"` + /// * `"solid_asp3` + /// * `"vita"` + /// * `"vxworks"` + /// * `"xous"` + /// + /// </details> #[stable(feature = "env", since = "1.0.0")] pub const OS: &str = os::OS; - /// Specifies the filename prefix used for shared libraries on this - /// platform. Example value is `lib`. - /// - /// Some possible values: - /// - /// - lib - /// - `""` (an empty string) + /// Specifies the filename prefix, if any, used for shared libraries on this platform. + /// This is either `"lib"` or an empty string. (`""`). #[stable(feature = "env", since = "1.0.0")] pub const DLL_PREFIX: &str = os::DLL_PREFIX; - /// Specifies the filename suffix used for shared libraries on this - /// platform. Example value is `.so`. - /// - /// Some possible values: + /// Specifies the filename suffix, if any, used for shared libraries on this platform. + /// An example value may be: `".so"`, `".elf"`, or `".dll"`. /// - /// - .so - /// - .dylib - /// - .dll + /// The possible values are identical to those of [`DLL_EXTENSION`], but with the leading period included. #[stable(feature = "env", since = "1.0.0")] pub const DLL_SUFFIX: &str = os::DLL_SUFFIX; - /// Specifies the file extension used for shared libraries on this - /// platform that goes after the dot. Example value is `so`. + /// Specifies the file extension, if any, used for shared libraries on this platform that goes after the dot. + /// An example value may be: `"so"`, `"elf"`, or `"dll"`. + /// + /// <details><summary>Full list of possible values</summary> /// - /// Some possible values: + /// * `"so"` + /// * `"dylib"` + /// * `"dll"` + /// * `"sgxs"` + /// * `"a"` + /// * `"elf"` + /// * `"wasm"` + /// * `""` (an empty string) /// - /// - so - /// - dylib - /// - dll + /// </details> #[stable(feature = "env", since = "1.0.0")] pub const DLL_EXTENSION: &str = os::DLL_EXTENSION; - /// Specifies the filename suffix used for executable binaries on this - /// platform. Example value is `.exe`. + /// Specifies the filename suffix, if any, used for executable binaries on this platform. + /// An example value may be: `".exe"`, or `".efi"`. /// - /// Some possible values: - /// - /// - .exe - /// - .nexe - /// - .pexe - /// - `""` (an empty string) + /// The possible values are identical to those of [`EXE_EXTENSION`], but with the leading period included. #[stable(feature = "env", since = "1.0.0")] pub const EXE_SUFFIX: &str = os::EXE_SUFFIX; - /// Specifies the file extension, if any, used for executable binaries - /// on this platform. Example value is `exe`. + /// Specifies the file extension, if any, used for executable binaries on this platform. + /// An example value may be: `"exe"`, or an empty string (`""`). + /// + /// <details><summary>Full list of possible values</summary> /// - /// Some possible values: + /// * `"exe"` + /// * `"efi"` + /// * `"js"` + /// * `"sgxs"` + /// * `"elf"` + /// * `"wasm"` + /// * `""` (an empty string) /// - /// - exe - /// - `""` (an empty string) + /// </details> #[stable(feature = "env", since = "1.0.0")] pub const EXE_EXTENSION: &str = os::EXE_EXTENSION; } diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 3e17431af45..b3e63aaf1c5 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -7,7 +7,7 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] pub use core::error::Error; #[unstable(feature = "error_generic_member_access", issue = "99301")] -pub use core::error::{request_ref, request_value, Request}; +pub use core::error::{Request, request_ref, request_value}; use crate::backtrace::Backtrace; use crate::fmt::{self, Write}; diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index f6df6259137..b436fe9929c 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -248,11 +248,16 @@ impl f128 { /// Returns a number composed of the magnitude of `self` and the sign of /// `sign`. /// - /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise - /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of - /// `sign` is returned. Note, however, that conserving the sign bit on NaN - /// across arithmetical operations is not generally guaranteed. - /// See [explanation of NaN as a special value](primitive@f128) for more info. + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`. + /// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is + /// returned. + /// + /// If `sign` is a NaN, then this operation will still carry over its sign into the result. Note + /// that IEEE 754 doesn't assign any meaning to the sign bit in case of a NaN, and as Rust + /// doesn't guarantee that the bit pattern of NaNs are conserved over arithmetic operations, the + /// result of `copysign` with `sign` being a NaN might produce an unexpected or non-portable + /// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more + /// info. /// /// # Examples /// diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index 10908332762..b2cd5fae9d0 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -247,11 +247,16 @@ impl f16 { /// Returns a number composed of the magnitude of `self` and the sign of /// `sign`. /// - /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise - /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of - /// `sign` is returned. Note, however, that conserving the sign bit on NaN - /// across arithmetical operations is not generally guaranteed. - /// See [explanation of NaN as a special value](primitive@f16) for more info. + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`. + /// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is + /// returned. + /// + /// If `sign` is a NaN, then this operation will still carry over its sign into the result. Note + /// that IEEE 754 doesn't assign any meaning to the sign bit in case of a NaN, and as Rust + /// doesn't guarantee that the bit pattern of NaNs are conserved over arithmetic operations, the + /// result of `copysign` with `sign` being a NaN might produce an unexpected or non-portable + /// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more + /// info. /// /// # Examples /// diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 12433d25bfa..fa0b3ef6484 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -18,8 +18,8 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::f32::{ - consts, DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, - MIN_EXP, MIN_POSITIVE, NAN, NEG_INFINITY, RADIX, + DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, MIN_EXP, + MIN_POSITIVE, NAN, NEG_INFINITY, RADIX, consts, }; #[cfg(not(test))] @@ -226,11 +226,16 @@ impl f32 { /// Returns a number composed of the magnitude of `self` and the sign of /// `sign`. /// - /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise - /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of - /// `sign` is returned. Note, however, that conserving the sign bit on NaN - /// across arithmetical operations is not generally guaranteed. - /// See [explanation of NaN as a special value](primitive@f32) for more info. + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`. + /// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is + /// returned. + /// + /// If `sign` is a NaN, then this operation will still carry over its sign into the result. Note + /// that IEEE 754 doesn't assign any meaning to the sign bit in case of a NaN, and as Rust + /// doesn't guarantee that the bit pattern of NaNs are conserved over arithmetic operations, the + /// result of `copysign` with `sign` being a NaN might produce an unexpected or non-portable + /// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more + /// info. /// /// # Examples /// diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index 3a4c1c120a4..99cfcfb231d 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -2,31 +2,24 @@ use crate::f32::consts; use crate::num::{FpCategory as Fp, *}; /// Smallest number -#[allow(dead_code)] // unused on x86 const TINY_BITS: u32 = 0x1; /// Next smallest number -#[allow(dead_code)] // unused on x86 const TINY_UP_BITS: u32 = 0x2; /// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -#[allow(dead_code)] // unused on x86 const MAX_DOWN_BITS: u32 = 0x7f7f_fffe; /// Zeroed exponent, full significant -#[allow(dead_code)] // unused on x86 const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff; /// Exponent = 0b1, zeroed significand -#[allow(dead_code)] // unused on x86 const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000; /// First pattern over the mantissa -#[allow(dead_code)] // unused on x86 const NAN_MASK1: u32 = 0x002a_aaaa; /// Second pattern over the mantissa -#[allow(dead_code)] // unused on x86 const NAN_MASK2: u32 = 0x0055_5555; #[allow(unused_macros)] @@ -353,9 +346,6 @@ fn test_is_sign_negative() { assert!((-f32::NAN).is_sign_negative()); } -// Ignore test on x87 floating point, these platforms do not guarantee NaN -// payloads are preserved and flush denormals to zero, failing the tests. -#[cfg(not(target_arch = "x86"))] #[test] fn test_next_up() { let tiny = f32::from_bits(TINY_BITS); @@ -386,9 +376,6 @@ fn test_next_up() { assert_f32_biteq!(nan2.next_up(), nan2); } -// Ignore test on x87 floating point, these platforms do not guarantee NaN -// payloads are preserved and flush denormals to zero, failing the tests. -#[cfg(not(target_arch = "x86"))] #[test] fn test_next_down() { let tiny = f32::from_bits(TINY_BITS); diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index a343e19173e..9fa43a6742e 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -18,8 +18,8 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::f64::{ - consts, DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, - MIN_EXP, MIN_POSITIVE, NAN, NEG_INFINITY, RADIX, + DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, MIN_EXP, + MIN_POSITIVE, NAN, NEG_INFINITY, RADIX, consts, }; #[cfg(not(test))] @@ -226,11 +226,16 @@ impl f64 { /// Returns a number composed of the magnitude of `self` and the sign of /// `sign`. /// - /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise - /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of - /// `sign` is returned. Note, however, that conserving the sign bit on NaN - /// across arithmetical operations is not generally guaranteed. - /// See [explanation of NaN as a special value](primitive@f32) for more info. + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`. + /// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is + /// returned. + /// + /// If `sign` is a NaN, then this operation will still carry over its sign into the result. Note + /// that IEEE 754 doesn't assign any meaning to the sign bit in case of a NaN, and as Rust + /// doesn't guarantee that the bit pattern of NaNs are conserved over arithmetic operations, the + /// result of `copysign` with `sign` being a NaN might produce an unexpected or non-portable + /// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more + /// info. /// /// # Examples /// diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index bac8405f973..3fac2efe0d7 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -2,31 +2,24 @@ use crate::f64::consts; use crate::num::{FpCategory as Fp, *}; /// Smallest number -#[allow(dead_code)] // unused on x86 const TINY_BITS: u64 = 0x1; /// Next smallest number -#[allow(dead_code)] // unused on x86 const TINY_UP_BITS: u64 = 0x2; /// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -#[allow(dead_code)] // unused on x86 const MAX_DOWN_BITS: u64 = 0x7fef_ffff_ffff_fffe; /// Zeroed exponent, full significant -#[allow(dead_code)] // unused on x86 const LARGEST_SUBNORMAL_BITS: u64 = 0x000f_ffff_ffff_ffff; /// Exponent = 0b1, zeroed significand -#[allow(dead_code)] // unused on x86 const SMALLEST_NORMAL_BITS: u64 = 0x0010_0000_0000_0000; /// First pattern over the mantissa -#[allow(dead_code)] // unused on x86 const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; /// Second pattern over the mantissa -#[allow(dead_code)] // unused on x86 const NAN_MASK2: u64 = 0x0005_5555_5555_5555; #[allow(unused_macros)] @@ -343,9 +336,6 @@ fn test_is_sign_negative() { assert!((-f64::NAN).is_sign_negative()); } -// Ignore test on x87 floating point, these platforms do not guarantee NaN -// payloads are preserved and flush denormals to zero, failing the tests. -#[cfg(not(target_arch = "x86"))] #[test] fn test_next_up() { let tiny = f64::from_bits(TINY_BITS); @@ -375,9 +365,6 @@ fn test_next_up() { assert_f64_biteq!(nan2.next_up(), nan2); } -// Ignore test on x87 floating point, these platforms do not guarantee NaN -// payloads are preserved and flush denormals to zero, failing the tests. -#[cfg(not(target_arch = "x86"))] #[test] fn test_next_down() { let tiny = f64::from_bits(TINY_BITS); diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index 2b67750c2f0..469136be883 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -166,11 +166,6 @@ pub mod c_str; #[stable(feature = "core_c_void", since = "1.30.0")] pub use core::ffi::c_void; -#[stable(feature = "core_ffi_c", since = "1.64.0")] -pub use core::ffi::{ - c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, - c_ulong, c_ulonglong, c_ushort, -}; #[unstable( feature = "c_variadic", reason = "the `c_variadic` feature has not been properly tested on \ @@ -178,6 +173,11 @@ pub use core::ffi::{ issue = "44930" )] pub use core::ffi::{VaList, VaListImpl}; +#[stable(feature = "core_ffi_c", since = "1.64.0")] +pub use core::ffi::{ + c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, + c_ulong, c_ulonglong, c_ushort, +}; #[doc(no_inline)] #[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 918eec2d0d8..0f905803bb8 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -196,6 +196,7 @@ impl OsString { /// let os_str = OsStr::new("foo"); /// assert_eq!(os_string.as_os_str(), os_str); /// ``` + #[cfg_attr(not(test), rustc_diagnostic_item = "os_string_as_os_str")] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] @@ -852,7 +853,7 @@ impl OsStr { /// Converts an `OsStr` to a <code>[Cow]<[str]></code>. /// - /// Any non-Unicode sequences are replaced with + /// Any non-UTF-8 sequences are replaced with /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. /// /// [U+FFFD]: crate::char::REPLACEMENT_CHARACTER @@ -918,6 +919,7 @@ impl OsStr { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] + #[cfg_attr(not(test), rustc_diagnostic_item = "os_str_to_os_string")] pub fn to_os_string(&self) -> OsString { OsString { inner: self.inner.to_owned() } } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 6a0d9f47960..55f3b628ce8 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -466,6 +466,7 @@ impl File { /// ``` #[must_use] #[stable(feature = "with_options", since = "1.58.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "file_options")] pub fn options() -> OpenOptions { OpenOptions::new() } @@ -835,7 +836,7 @@ impl Read for &File { } #[stable(feature = "rust1", since = "1.0.0")] impl Write for &File { - /// Writes some bytes from the file. + /// Writes some bytes to the file. /// /// See [`Write::write`] docs for more info. /// @@ -1009,6 +1010,7 @@ impl OpenOptions { /// let mut options = OpenOptions::new(); /// let file = options.read(true).open("foo.txt"); /// ``` + #[cfg_attr(not(test), rustc_diagnostic_item = "open_options_new")] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] pub fn new() -> Self { @@ -1989,6 +1991,11 @@ impl AsInner<fs_imp::DirEntry> for DirEntry { /// * The file doesn't exist. /// * The user lacks permissions to remove the file. /// +/// This function will only ever return an error of kind `NotFound` if the given +/// path does not exist. Note that the inverse is not true, +/// ie. if a path does not exist, its removal may fail for a number of reasons, +/// such as insufficient permissions. +/// /// # Examples /// /// ```no_run @@ -2446,6 +2453,11 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { /// * The user lacks permissions to remove the directory at the provided `path`. /// * The directory isn't empty. /// +/// This function will only ever return an error of kind `NotFound` if the given +/// path does not exist. Note that the inverse is not true, +/// ie. if a path does not exist, its removal may fail for a number of reasons, +/// such as insufficient permissions. +/// /// # Examples /// /// ```no_run @@ -2471,16 +2483,15 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { /// # Platform-specific behavior /// /// This function currently corresponds to `openat`, `fdopendir`, `unlinkat` and `lstat` functions -/// on Unix (except for macOS before version 10.10 and REDOX) and the `CreateFileW`, -/// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtCreateFile` functions on -/// Windows. Note that, this [may change in the future][changes]. +/// on Unix (except for REDOX) and the `CreateFileW`, `GetFileInformationByHandleEx`, +/// `SetFileInformationByHandle`, and `NtCreateFile` functions on Windows. Note that, this +/// [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior /// -/// On macOS before version 10.10 and REDOX, as well as when running in Miri for any target, this -/// function is not protected against time-of-check to time-of-use (TOCTOU) race conditions, and -/// should not be used in security-sensitive code on those platforms. All other platforms are -/// protected. +/// On REDOX, as well as when running in Miri for any target, this function is not protected against +/// time-of-check to time-of-use (TOCTOU) race conditions, and should not be used in +/// security-sensitive code on those platforms. All other platforms are protected. /// /// # Errors /// diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 13028c4c3b5..412603ddea3 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1,7 +1,5 @@ use rand::RngCore; -#[cfg(target_os = "macos")] -use crate::ffi::{c_char, c_int}; use crate::fs::{self, File, FileTimes, OpenOptions}; use crate::io::prelude::*; use crate::io::{BorrowedBuf, ErrorKind, SeekFrom}; @@ -13,12 +11,10 @@ use crate::os::unix::fs::symlink as symlink_file; #[cfg(unix)] use crate::os::unix::fs::symlink as junction_point; #[cfg(windows)] -use crate::os::windows::fs::{junction_point, symlink_dir, symlink_file, OpenOptionsExt}; +use crate::os::windows::fs::{OpenOptionsExt, junction_point, symlink_dir, symlink_file}; use crate::path::Path; use crate::sync::Arc; -#[cfg(target_os = "macos")] -use crate::sys::weak::weak; -use crate::sys_common::io::test::{tmpdir, TempDir}; +use crate::sys_common::io::test::{TempDir, tmpdir}; use crate::time::{Duration, Instant, SystemTime}; use crate::{env, str, thread}; @@ -80,17 +76,6 @@ pub fn got_symlink_permission(tmpdir: &TempDir) -> bool { } } -#[cfg(target_os = "macos")] -fn able_to_not_follow_symlinks_while_hard_linking() -> bool { - weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int); - linkat.get().is_some() -} - -#[cfg(not(target_os = "macos"))] -fn able_to_not_follow_symlinks_while_hard_linking() -> bool { - return true; -} - #[test] fn file_test_io_smoke_test() { let message = "it's alright. have a good time"; @@ -1456,9 +1441,6 @@ fn symlink_hard_link() { if !got_symlink_permission(&tmpdir) { return; }; - if !able_to_not_follow_symlinks_while_hard_linking() { - return; - } // Create "file", a file. check!(fs::File::create(tmpdir.join("file"))); diff --git a/library/std/src/hash/random.rs b/library/std/src/hash/random.rs index 8ef45172eac..40f3a90f60c 100644 --- a/library/std/src/hash/random.rs +++ b/library/std/src/hash/random.rs @@ -10,7 +10,8 @@ #[allow(deprecated)] use super::{BuildHasher, Hasher, SipHasher13}; use crate::cell::Cell; -use crate::{fmt, sys}; +use crate::fmt; +use crate::sys::random::hashmap_random_keys; /// `RandomState` is the default state for [`HashMap`] types. /// @@ -65,7 +66,7 @@ impl RandomState { // increment one of the seeds on every RandomState creation, giving // every corresponding HashMap a different iteration order. thread_local!(static KEYS: Cell<(u64, u64)> = { - Cell::new(sys::hashmap_random_keys()) + Cell::new(hashmap_random_keys()) }); KEYS.with(|keys| { diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 0b12e5777c8..e51dde994de 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -4,8 +4,8 @@ use buffer::Buffer; use crate::fmt; use crate::io::{ - self, uninlined_slow_read_byte, BorrowedCursor, BufRead, IoSliceMut, Read, Seek, SeekFrom, - SizeHint, SpecReadByte, DEFAULT_BUF_SIZE, + self, BorrowedCursor, BufRead, DEFAULT_BUF_SIZE, IoSliceMut, Read, Seek, SeekFrom, SizeHint, + SpecReadByte, uninlined_slow_read_byte, }; /// The `BufReader<R>` struct adds buffering to any reader. @@ -94,10 +94,15 @@ impl<R: Read> BufReader<R> { pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> { BufReader { inner, buf: Buffer::with_capacity(capacity) } } +} +impl<R: Read + ?Sized> BufReader<R> { /// Attempt to look ahead `n` bytes. /// - /// `n` must be less than `capacity`. + /// `n` must be less than or equal to `capacity`. + /// + /// the returned slice may be less than `n` bytes long if + /// end of file is reached. /// /// ## Examples /// @@ -115,6 +120,7 @@ impl<R: Read> BufReader<R> { /// let mut s = String::new(); /// rdr.read_to_string(&mut s).unwrap(); /// assert_eq!(&s, "hello"); + /// assert_eq!(rdr.peek(1).unwrap().len(), 0); /// ``` #[unstable(feature = "bufreader_peek", issue = "128405")] pub fn peek(&mut self, n: usize) -> io::Result<&[u8]> { @@ -123,7 +129,11 @@ impl<R: Read> BufReader<R> { if self.buf.pos() > 0 { self.buf.backshift(); } - self.buf.read_more(&mut self.inner)?; + let new = self.buf.read_more(&mut self.inner)?; + if new == 0 { + // end of file, no more bytes to read + return Ok(&self.buf.buffer()[..]); + } debug_assert_eq!(self.buf.pos(), 0); } Ok(&self.buf.buffer()[..n]) @@ -265,6 +275,7 @@ impl<R: ?Sized> BufReader<R> { // This is only used by a test which asserts that the initialization-tracking is correct. #[cfg(test)] impl<R: ?Sized> BufReader<R> { + #[allow(missing_docs)] pub fn initialized(&self) -> usize { self.buf.initialized() } diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index ccd67fafb45..1bf84d8bef3 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -98,7 +98,7 @@ impl Buffer { } /// Read more bytes into the buffer without discarding any of its contents - pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<()> { + pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<usize> { let mut buf = BorrowedBuf::from(&mut self.buf[self.pos..]); let old_init = self.initialized - self.pos; unsafe { @@ -107,7 +107,7 @@ impl Buffer { reader.read_buf(buf.unfilled())?; self.filled += buf.len(); self.initialized += buf.init_len() - old_init; - Ok(()) + Ok(buf.len()) } /// Remove bytes that have already been read from the buffer. diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 21650d46744..13516d3b961 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -1,5 +1,5 @@ use crate::io::{ - self, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE, + self, DEFAULT_BUF_SIZE, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, }; use crate::mem::{self, ManuallyDrop}; use crate::{error, fmt, ptr}; diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index d49866345cb..8d733325b3b 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -1,4 +1,4 @@ -use super::{BorrowedBuf, BufReader, BufWriter, Read, Result, Write, DEFAULT_BUF_SIZE}; +use super::{BorrowedBuf, BufReader, BufWriter, DEFAULT_BUF_SIZE, Read, Result, Write}; use crate::alloc::Allocator; use crate::cmp; use crate::collections::VecDeque; diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs index 7e08826a7e1..2e0eb6cdce6 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/std/src/io/copy/tests.rs @@ -122,8 +122,8 @@ mod io_benches { use test::Bencher; use crate::fs::{File, OpenOptions}; - use crate::io::prelude::*; use crate::io::BufReader; + use crate::io::prelude::*; #[bench] fn bench_copy_buf_reader(b: &mut Bencher) { diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index e8ae1d99fbf..6ecd9469c17 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -223,10 +223,10 @@ pub enum ErrorKind { #[stable(feature = "rust1", since = "1.0.0")] ConnectionReset, /// The remote host is not reachable. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] HostUnreachable, /// The network containing the remote host is not reachable. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] NetworkUnreachable, /// The connection was aborted (terminated) by the remote server. #[stable(feature = "rust1", since = "1.0.0")] @@ -243,7 +243,7 @@ pub enum ErrorKind { #[stable(feature = "rust1", since = "1.0.0")] AddrNotAvailable, /// The system's networking is down. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] NetworkDown, /// The operation failed because a pipe was closed. #[stable(feature = "rust1", since = "1.0.0")] @@ -259,18 +259,18 @@ pub enum ErrorKind { /// /// For example, a filesystem path was specified where one of the intermediate directory /// components was, in fact, a plain file. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] NotADirectory, /// The filesystem object is, unexpectedly, a directory. /// /// A directory was specified when a non-directory was expected. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] IsADirectory, /// A non-empty directory was specified where an empty directory was expected. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] DirectoryNotEmpty, /// The filesystem or storage medium is read-only, but a write operation was attempted. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] ReadOnlyFilesystem, /// Loop in the filesystem or IO subsystem; often, too many levels of symbolic links. /// @@ -285,7 +285,7 @@ pub enum ErrorKind { /// /// With some network filesystems, notably NFS, an open file (or directory) can be invalidated /// by problems with the network or server. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] StaleNetworkFileHandle, /// A parameter was incorrect. #[stable(feature = "rust1", since = "1.0.0")] @@ -319,13 +319,13 @@ pub enum ErrorKind { /// The underlying storage (typically, a filesystem) is full. /// /// This does not include out of quota errors. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] StorageFull, /// Seek on unseekable file. /// /// Seeking was attempted on an open file handle which is not suitable for seeking - for /// example, on Unix, a named pipe opened with `File::open`. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] NotSeekable, /// Filesystem quota was exceeded. #[unstable(feature = "io_error_more", issue = "86442")] @@ -335,22 +335,22 @@ pub enum ErrorKind { /// This might arise from a hard limit of the underlying filesystem or file access API, or from /// an administratively imposed resource limitation. Simple disk full, and out of quota, have /// their own errors. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] FileTooLarge, /// Resource is busy. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] ResourceBusy, /// Executable file is busy. /// /// An attempt was made to write to a file which is also in use as a running program. (Not all /// operating systems detect this situation.) - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] ExecutableFileBusy, /// Deadlock (avoided). /// /// A file locking operation would result in deadlock. This situation is typically detected, if /// at all, on a best-effort basis. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] Deadlock, /// Cross-device or cross-filesystem (hard) link or rename. #[unstable(feature = "io_error_more", issue = "86442")] @@ -358,7 +358,7 @@ pub enum ErrorKind { /// Too many (hard) links to the same filesystem object. /// /// The filesystem does not support making so many hardlinks to the same file. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] TooManyLinks, /// A filename was invalid. /// @@ -369,7 +369,7 @@ pub enum ErrorKind { /// /// When trying to run an external program, a system or process limit on the size of the /// arguments would have been exceeded. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_a_bit_more", since = "CURRENT_RUSTC_VERSION")] ArgumentListTooLong, /// This operation was interrupted. /// diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index 064e2e36b7a..00d04984a38 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,4 +1,4 @@ -use super::{const_io_error, Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage}; +use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_io_error}; use crate::assert_matches::assert_matches; use crate::mem::size_of; use crate::sys::decode_error_kind; diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 644b294db8d..0b57d01f273 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -307,9 +307,9 @@ pub(crate) use error::const_io_error; pub use self::buffered::WriterPanicked; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use self::error::RawOsError; -pub(crate) use self::stdio::attempt_print_to_stderr; #[stable(feature = "is_terminal", since = "1.70.0")] pub use self::stdio::IsTerminal; +pub(crate) use self::stdio::attempt_print_to_stderr; #[unstable(feature = "print_internals", issue = "none")] #[doc(hidden)] pub use self::stdio::{_eprint, _print}; @@ -322,8 +322,8 @@ pub use self::{ copy::copy, cursor::Cursor, error::{Error, ErrorKind, Result}, - stdio::{stderr, stdin, stdout, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock}, - util::{empty, repeat, sink, Empty, Repeat, Sink}, + stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, + util::{Empty, Repeat, Sink, empty, repeat, sink}, }; use crate::mem::take; use crate::ops::{Deref, DerefMut}; @@ -398,8 +398,7 @@ where // - avoid passing large buffers to readers that always initialize the free capacity if they perform short reads (#23815, #23820) // - pass large buffers to readers that do not initialize the spare capacity. this can amortize per-call overheads // - and finally pass not-too-small and not-too-large buffers to Windows read APIs because they manage to suffer from both problems -// at the same time, i.e. small reads suffer from syscall overhead, all reads incur initialization cost -// proportional to buffer size (#110650) +// at the same time, i.e. small reads suffer from syscall overhead, all reads incur costs proportional to buffer size (#110650) // pub(crate) fn default_read_to_end<R: Read + ?Sized>( r: &mut R, @@ -444,6 +443,8 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>( } } + let mut consecutive_short_reads = 0; + loop { if buf.len() == buf.capacity() && buf.capacity() == start_cap { // The buffer might be an exact fit. Let's read into a probe buffer @@ -489,6 +490,12 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>( return Ok(buf.len() - start_len); } + if bytes_read < buf_len { + consecutive_short_reads += 1; + } else { + consecutive_short_reads = 0; + } + // store how much was initialized but not filled initialized = unfilled_but_initialized; @@ -503,7 +510,10 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>( // The reader is returning short reads but it doesn't call ensure_init(). // In that case we no longer need to restrict read sizes to avoid // initialization costs. - if !was_fully_initialized { + // When reading from disk we usually don't get any short reads except at EOF. + // So we wait for at least 2 short reads before uncapping the read buffer; + // this helps with the Windows issue. + if !was_fully_initialized && consecutive_short_reads > 1 { max_read_size = usize::MAX; } @@ -2058,6 +2068,7 @@ pub trait Seek { /// It is used by the [`Seek`] trait. #[derive(Copy, PartialEq, Eq, Clone, Debug)] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "SeekFrom")] pub enum SeekFrom { /// Sets the offset to the provided number of bytes. #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/io/stdio/tests.rs b/library/std/src/io/stdio/tests.rs index f89fd27ce6c..ea76a271d12 100644 --- a/library/std/src/io/stdio/tests.rs +++ b/library/std/src/io/stdio/tests.rs @@ -159,8 +159,7 @@ where assert_eq!(rx2.recv().unwrap(), Release2); // release th2 th2.join().unwrap(); th1.join().unwrap(); - assert_eq!( - *log.lock().unwrap(), - [Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1] - ); + assert_eq!(*log.lock().unwrap(), [ + Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1 + ]); } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index bb6a53bb290..f551dcd401e 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -1,7 +1,7 @@ -use super::{repeat, BorrowedBuf, Cursor, SeekFrom}; +use super::{BorrowedBuf, Cursor, SeekFrom, repeat}; use crate::cmp::{self, min}; use crate::io::{ - self, BufRead, BufReader, IoSlice, IoSliceMut, Read, Seek, Write, DEFAULT_BUF_SIZE, + self, BufRead, BufReader, DEFAULT_BUF_SIZE, IoSlice, IoSliceMut, Read, Seek, Write, }; use crate::mem::MaybeUninit; use crate::ops::Deref; @@ -738,7 +738,7 @@ fn read_buf_full_read() { #[test] // Miri does not support signalling OOM #[cfg_attr(miri, ignore)] -// 64-bit only to be sure the allocator will fail fast on an impossible to satsify size +// 64-bit only to be sure the allocator will fail fast on an impossible to satisfy size #[cfg(target_pointer_width = "64")] fn try_oom_error() { let mut v = Vec::<u8>::new(); diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index 1dff3f3832b..0599a881af1 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -1,5 +1,5 @@ use crate::io::prelude::*; -use crate::io::{empty, repeat, sink, BorrowedBuf, Empty, Repeat, SeekFrom, Sink}; +use crate::io::{BorrowedBuf, Empty, Repeat, SeekFrom, Sink, empty, repeat, sink}; use crate::mem::MaybeUninit; #[test] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 2530a376387..4d93af6ea65 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -32,13 +32,17 @@ //! //! Once you are familiar with the contents of the standard library you may //! begin to find the verbosity of the prose distracting. At this stage in your -//! development you may want to press the `[-]` button near the top of the -//! page to collapse it into a more skimmable view. -//! -//! While you are looking at that `[-]` button also notice the `source` -//! link. Rust's API documentation comes with the source code and you are -//! encouraged to read it. The standard library source is generally high -//! quality and a peek behind the curtains is often enlightening. +//! development you may want to press the <code> +//! <svg style="width:0.75rem;height:0.75rem" viewBox="0 0 12 12" +//! stroke="currentColor" fill="none"> +//! <path d="M2,2l4,4l4,-4M2,6l4,4l4,-4"/></svg> Summary</code> button near the +//! top of the page to collapse it into a more skimmable view. +//! +//! While you are looking at the top of the page, also notice the +//! <code>source</code> link. Rust's API documentation comes with the source +//! code and you are encouraged to read it. The standard library source is +//! generally high quality and a peek behind the curtains is +//! often enlightening. //! //! # What is in the standard library documentation? //! @@ -272,7 +276,7 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(min_exhaustive_patterns))] +#![cfg_attr(bootstrap, feature(const_mut_refs))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] @@ -282,7 +286,6 @@ #![feature(cfg_target_thread_local)] #![feature(cfi_encoding)] #![feature(concat_idents)] -#![feature(const_mut_refs)] #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(doc_cfg)] @@ -306,10 +309,12 @@ #![feature(negative_impls)] #![feature(never_type)] #![feature(no_sanitize)] +#![feature(optimize_attribute)] #![feature(prelude_import)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] #![feature(staged_api)] +#![feature(stmt_expr_attributes)] #![feature(thread_local)] #![feature(try_blocks)] #![feature(type_alias_impl_trait)] @@ -317,6 +322,7 @@ // // Library features (core): // tidy-alphabetical-start +#![feature(array_chunks)] #![feature(c_str_module)] #![feature(char_internals)] #![feature(clone_to_uninit)] @@ -336,6 +342,7 @@ #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(ip)] +#![feature(lazy_get)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_write_slice)] #![feature(panic_can_unwind)] @@ -346,6 +353,7 @@ #![feature(prelude_2024)] #![feature(ptr_as_uninit)] #![feature(ptr_mask)] +#![feature(random)] #![feature(slice_internals)] #![feature(slice_ptr_get)] #![feature(slice_range)] @@ -362,7 +370,7 @@ #![feature(allocator_api)] #![feature(get_mut_unchecked)] #![feature(map_try_insert)] -#![feature(new_uninit)] +#![feature(new_zeroed_alloc)] #![feature(slice_concat_trait)] #![feature(thin_box)] #![feature(try_reserve_kind)] @@ -491,9 +499,9 @@ pub use core::default; pub use core::future; #[stable(feature = "core_hint", since = "1.27.0")] pub use core::hint; -#[stable(feature = "i128", since = "1.26.0")] +#[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] -pub use core::i128; +pub use core::i8; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::i16; @@ -503,9 +511,9 @@ pub use core::i32; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::i64; -#[stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "i128", since = "1.26.0")] #[allow(deprecated, deprecated_in_future)] -pub use core::i8; +pub use core::i128; #[stable(feature = "rust1", since = "1.0.0")] pub use core::intrinsics; #[stable(feature = "rust1", since = "1.0.0")] @@ -527,9 +535,9 @@ pub use core::pin; pub use core::ptr; #[stable(feature = "rust1", since = "1.0.0")] pub use core::result; -#[stable(feature = "i128", since = "1.26.0")] +#[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] -pub use core::u128; +pub use core::u8; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::u16; @@ -539,9 +547,9 @@ pub use core::u32; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::u64; -#[stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "i128", since = "1.26.0")] #[allow(deprecated, deprecated_in_future)] -pub use core::u8; +pub use core::u128; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::usize; @@ -593,6 +601,8 @@ pub mod path; #[unstable(feature = "anonymous_pipe", issue = "127154")] pub mod pipe; pub mod process; +#[unstable(feature = "random", issue = "130703")] +pub mod random; pub mod sync; pub mod time; @@ -647,9 +657,9 @@ pub mod arch { #[stable(feature = "simd_x86", since = "1.27.0")] pub use std_detect::is_x86_feature_detected; #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] - pub use std_detect::{is_mips64_feature_detected, is_mips_feature_detected}; + pub use std_detect::{is_mips_feature_detected, is_mips64_feature_detected}; #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] - pub use std_detect::{is_powerpc64_feature_detected, is_powerpc_feature_detected}; + pub use std_detect::{is_powerpc_feature_detected, is_powerpc64_feature_detected}; } // This was stabilized in the crate root so we have to keep it there. diff --git a/library/std/src/net/ip_addr/tests.rs b/library/std/src/net/ip_addr/tests.rs index ab99c0c2fcc..7bed6f8a0f5 100644 --- a/library/std/src/net/ip_addr/tests.rs +++ b/library/std/src/net/ip_addr/tests.rs @@ -1,5 +1,5 @@ -use crate::net::test::{sa4, tsa}; use crate::net::Ipv4Addr; +use crate::net::test::{sa4, tsa}; #[test] fn to_socket_addr_socketaddr() { diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 22d2dfe65a2..06ed4f6a03d 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -8,7 +8,7 @@ use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::iter::FusedIterator; use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sys_common::{net as net_imp, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; use crate::time::Duration; /// A TCP stream between a local and a remote socket. diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 32e9086003d..d4252cb87ac 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -4,7 +4,7 @@ mod tests; use crate::fmt; use crate::io::{self, ErrorKind}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; -use crate::sys_common::{net as net_imp, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; use crate::time::Duration; /// A UDP socket. diff --git a/library/std/src/num.rs b/library/std/src/num.rs index c1e6e7e628c..d2f679e7dde 100644 --- a/library/std/src/num.rs +++ b/library/std/src/num.rs @@ -26,9 +26,9 @@ pub use core::num::ZeroablePrimitive; #[stable(feature = "rust1", since = "1.0.0")] pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError}; #[stable(feature = "signed_nonzero", since = "1.34.0")] -pub use core::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize}; +pub use core::num::{NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize}; #[stable(feature = "nonzero", since = "1.28.0")] -pub use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; +pub use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize}; #[cfg(test)] use crate::fmt; diff --git a/library/std/src/os/fortanix_sgx/mod.rs b/library/std/src/os/fortanix_sgx/mod.rs index 64f4d97ca95..2b5ccbe98f1 100644 --- a/library/std/src/os/fortanix_sgx/mod.rs +++ b/library/std/src/os/fortanix_sgx/mod.rs @@ -22,12 +22,12 @@ pub mod usercalls { /// Lowest-level interfaces to usercalls and usercall ABI type definitions. pub mod raw { pub use crate::sys::abi::usercalls::raw::{ - accept_stream, alloc, async_queues, bind_stream, close, connect_stream, do_usercall, - exit, flush, free, insecure_time, launch_thread, read, read_alloc, send, wait, write, - ByteBuffer, Cancel, Error, Fd, FifoDescriptor, Register, RegisterArgument, Result, - Return, ReturnValue, Tcs, Usercall, Usercalls as UsercallNrs, EV_RETURNQ_NOT_EMPTY, - EV_UNPARK, EV_USERCALLQ_NOT_FULL, FD_STDERR, FD_STDIN, FD_STDOUT, RESULT_SUCCESS, - USERCALL_USER_DEFINED, WAIT_INDEFINITE, WAIT_NO, + ByteBuffer, Cancel, EV_RETURNQ_NOT_EMPTY, EV_UNPARK, EV_USERCALLQ_NOT_FULL, Error, + FD_STDERR, FD_STDIN, FD_STDOUT, Fd, FifoDescriptor, RESULT_SUCCESS, Register, + RegisterArgument, Result, Return, ReturnValue, Tcs, USERCALL_USER_DEFINED, Usercall, + Usercalls as UsercallNrs, WAIT_INDEFINITE, WAIT_NO, accept_stream, alloc, async_queues, + bind_stream, close, connect_stream, do_usercall, exit, flush, free, insecure_time, + launch_thread, read, read_alloc, send, wait, write, }; } } diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 020a8b324f4..a2496baa63f 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -143,6 +143,8 @@ pub mod nto; pub mod openbsd; #[cfg(target_os = "redox")] pub mod redox; +#[cfg(target_os = "rtems")] +pub mod rtems; #[cfg(target_os = "solaris")] pub mod solaris; #[cfg(target_os = "solid_asp3")] diff --git a/library/std/src/os/rtems/fs.rs b/library/std/src/os/rtems/fs.rs new file mode 100644 index 00000000000..bec0d41e42d --- /dev/null +++ b/library/std/src/os/rtems/fs.rs @@ -0,0 +1,374 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Returns the device ID on which this file resides. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_dev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + + /// Returns the inode number. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ino()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + + /// Returns the file type and mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mode()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + + /// Returns the number of hard links to file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_nlink()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + + /// Returns the user ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_uid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + + /// Returns the group ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_gid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + + /// Returns the device ID that this file represents. Only relevant for special file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_rdev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + + /// Returns the size of the file (if it is a regular file or a symbolic link) in bytes. + /// + /// The size of a symbolic link is the length of the pathname it contains, + /// without a terminating null byte. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_size()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + + /// Returns the last access time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + + /// Returns the last access time of the file, in nanoseconds since [`st_atime`]. + /// + /// [`st_atime`]: Self::st_atime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + + /// Returns the last modification time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + + /// Returns the last modification time of the file, in nanoseconds since [`st_mtime`]. + /// + /// [`st_mtime`]: Self::st_mtime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + + /// Returns the last status change time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + + /// Returns the last status change time of the file, in nanoseconds since [`st_ctime`]. + /// + /// [`st_ctime`]: Self::st_ctime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + + /// Returns the "preferred" block size for efficient filesystem I/O. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blksize()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + + /// Returns the number of blocks allocated to the file, 512-byte units. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blocks()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + + fn st_atime_nsec(&self) -> i64 { + 0 + } + + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + + fn st_mtime_nsec(&self) -> i64 { + 0 + } + + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + + fn st_ctime_nsec(&self) -> i64 { + 0 + } + + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } +} diff --git a/library/std/src/os/rtems/mod.rs b/library/std/src/os/rtems/mod.rs new file mode 100644 index 00000000000..7275bfd1765 --- /dev/null +++ b/library/std/src/os/rtems/mod.rs @@ -0,0 +1,4 @@ +#![stable(feature = "raw_ext", since = "1.1.0")] +#![forbid(unsafe_op_in_unsafe_fn)] +pub mod fs; +pub(crate) mod raw; diff --git a/library/std/src/os/rtems/raw.rs b/library/std/src/os/rtems/raw.rs new file mode 100644 index 00000000000..113079cf4ab --- /dev/null +++ b/library/std/src/os/rtems/raw.rs @@ -0,0 +1,33 @@ +//! rtems raw type definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![deprecated( + since = "1.8.0", + note = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions" +)] +#![allow(deprecated)] + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = libc::pthread_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blkcnt_t = libc::blkcnt_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blksize_t = libc::blksize_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = libc::dev_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type ino_t = libc::ino_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = libc::mode_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type nlink_t = libc::nlink_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type off_t = libc::off_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type time_t = libc::time_t; diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index caf6980afd9..a964db2e0ac 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -334,6 +334,7 @@ pub trait PermissionsExt { /// assert_eq!(permissions.mode(), 0o644); /// ``` #[stable(feature = "fs_ext", since = "1.1.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "permissions_from_mode")] fn from_mode(mode: u32) -> Self; } diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index c6581b9c4c8..7d2f0bd4efe 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -73,6 +73,8 @@ mod platform { pub use crate::os::openbsd::*; #[cfg(target_os = "redox")] pub use crate::os::redox::*; + #[cfg(target_os = "rtems")] + pub use crate::os::rtems::*; #[cfg(target_os = "solaris")] pub use crate::os::solaris::*; #[cfg(target_os = "vita")] diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs index 9b487a62982..c34a3b4e184 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary.rs @@ -1,6 +1,6 @@ // FIXME: This is currently disabled on *BSD. -use super::{sockaddr_un, SocketAddr}; +use super::{SocketAddr, sockaddr_un}; use crate::io::{self, IoSlice, IoSliceMut}; use crate::marker::PhantomData; use crate::mem::zeroed; diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index a605c3d4a26..48aaddd2d52 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -12,9 +12,9 @@ ))] use libc::MSG_NOSIGNAL; +use super::{SocketAddr, sockaddr_un}; #[cfg(any(doc, target_os = "android", target_os = "linux"))] -use super::{recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to, SocketAncillary}; -use super::{sockaddr_un, SocketAddr}; +use super::{SocketAncillary, recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to}; #[cfg(any(doc, target_os = "android", target_os = "linux"))] use crate::io::{IoSlice, IoSliceMut}; use crate::net::Shutdown; diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index a55199c82fc..440408eb13f 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -1,4 +1,4 @@ -use super::{sockaddr_un, SocketAddr, UnixStream}; +use super::{SocketAddr, UnixStream, sockaddr_un}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; use crate::sys::cvt; diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 19fc7b3d853..4967c5b89ec 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -1,3 +1,6 @@ +use super::{SocketAddr, sockaddr_un}; +#[cfg(any(doc, target_os = "android", target_os = "linux"))] +use super::{SocketAncillary, recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to}; #[cfg(any( target_os = "android", target_os = "linux", @@ -8,10 +11,7 @@ target_os = "nto", target_vendor = "apple", ))] -use super::{peer_cred, UCred}; -#[cfg(any(doc, target_os = "android", target_os = "linux"))] -use super::{recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to, SocketAncillary}; -use super::{sockaddr_un, SocketAddr}; +use super::{UCred, peer_cred}; use crate::fmt; use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::Shutdown; diff --git a/library/std/src/os/unix/net/ucred.rs b/library/std/src/os/unix/net/ucred.rs index b96e373ad0a..c818bd05858 100644 --- a/library/std/src/os/unix/net/ucred.rs +++ b/library/std/src/os/unix/net/ucred.rs @@ -38,7 +38,7 @@ pub(super) use self::impl_linux::peer_cred; #[cfg(any(target_os = "linux", target_os = "android"))] mod impl_linux { - use libc::{c_void, getsockopt, socklen_t, ucred, SOL_SOCKET, SO_PEERCRED}; + use libc::{SO_PEERCRED, SOL_SOCKET, c_void, getsockopt, socklen_t, ucred}; use super::UCred; use crate::os::unix::io::AsRawFd; @@ -98,7 +98,7 @@ mod impl_bsd { #[cfg(target_vendor = "apple")] mod impl_apple { - use libc::{c_void, getpeereid, getsockopt, pid_t, socklen_t, LOCAL_PEERPID, SOL_LOCAL}; + use libc::{LOCAL_PEERPID, SOL_LOCAL, c_void, getpeereid, getsockopt, pid_t, socklen_t}; use super::UCred; use crate::os::unix::io::AsRawFd; diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 46202441d4e..9aadd949116 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -118,11 +118,7 @@ pub trait CommandExt: Sealed { /// [`pre_exec`]: CommandExt::pre_exec #[stable(feature = "process_exec", since = "1.15.0")] #[deprecated(since = "1.37.0", note = "should be unsafe, use `pre_exec` instead")] - #[cfg_attr(bootstrap, rustc_deprecated_safe_2024)] - #[cfg_attr( - not(bootstrap), - rustc_deprecated_safe_2024(audit_that = "the closure is async-signal-safe") - )] + #[rustc_deprecated_safe_2024(audit_that = "the closure is async-signal-safe")] unsafe fn before_exec<F>(&mut self, f: F) -> &mut process::Command where F: FnMut() -> io::Result<()> + Send + Sync + 'static, diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 3dcde43cfec..ddb8dbd8fee 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -298,7 +298,7 @@ impl OpenOptionsExt for OpenOptions { /// of the [`BY_HANDLE_FILE_INFORMATION`] structure. /// /// [`BY_HANDLE_FILE_INFORMATION`]: -/// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-by_handle_file_information +/// https://docs.microsoft.com/windows/win32/api/fileapi/ns-fileapi-by_handle_file_information #[stable(feature = "metadata_ext", since = "1.1.0")] pub trait MetadataExt { /// Returns the value of the `dwFileAttributes` field of this metadata. @@ -322,7 +322,7 @@ pub trait MetadataExt { /// ``` /// /// [File Attribute Constants]: - /// https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants + /// https://docs.microsoft.com/windows/win32/fileio/file-attribute-constants #[stable(feature = "metadata_ext", since = "1.1.0")] fn file_attributes(&self) -> u32; @@ -351,7 +351,7 @@ pub trait MetadataExt { /// } /// ``` /// - /// [`FILETIME`]: https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime + /// [`FILETIME`]: https://docs.microsoft.com/windows/win32/api/minwinbase/ns-minwinbase-filetime #[stable(feature = "metadata_ext", since = "1.1.0")] fn creation_time(&self) -> u64; @@ -386,7 +386,7 @@ pub trait MetadataExt { /// } /// ``` /// - /// [`FILETIME`]: https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime + /// [`FILETIME`]: https://docs.microsoft.com/windows/win32/api/minwinbase/ns-minwinbase-filetime #[stable(feature = "metadata_ext", since = "1.1.0")] fn last_access_time(&self) -> u64; @@ -419,11 +419,11 @@ pub trait MetadataExt { /// } /// ``` /// - /// [`FILETIME`]: https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime + /// [`FILETIME`]: https://docs.microsoft.com/windows/win32/api/minwinbase/ns-minwinbase-filetime #[stable(feature = "metadata_ext", since = "1.1.0")] fn last_write_time(&self) -> u64; - /// Returns the value of the `nFileSize{High,Low}` fields of this + /// Returns the value of the `nFileSize` fields of this /// metadata. /// /// The returned value does not have meaning for directories. @@ -462,7 +462,7 @@ pub trait MetadataExt { #[unstable(feature = "windows_by_handle", issue = "63010")] fn number_of_links(&self) -> Option<u32>; - /// Returns the value of the `nFileIndex{Low,High}` fields of this + /// Returns the value of the `nFileIndex` fields of this /// metadata. /// /// This will return `None` if the `Metadata` instance was created from a @@ -471,10 +471,14 @@ pub trait MetadataExt { #[unstable(feature = "windows_by_handle", issue = "63010")] fn file_index(&self) -> Option<u64>; - /// Returns the change time, which is the last time file metadata was changed, such as - /// renames, attributes, etc + /// Returns the value of the `ChangeTime` fields of this metadata. /// - /// This will return `None` if the `Metadata` instance was not created using the `FILE_BASIC_INFO` type. + /// `ChangeTime` is the last time file metadata was changed, such as + /// renames, attributes, etc. + /// + /// This will return `None` if `Metadata` instance was created from a call to + /// `DirEntry::metadata` or if the `target_vendor` is outside the current platform + /// support for this api. #[unstable(feature = "windows_change_time", issue = "121478")] fn change_time(&self) -> Option<u64>; } diff --git a/library/std/src/os/xous/ffi/definitions.rs b/library/std/src/os/xous/ffi/definitions.rs index 345005bcc78..1b16849af03 100644 --- a/library/std/src/os/xous/ffi/definitions.rs +++ b/library/std/src/os/xous/ffi/definitions.rs @@ -126,42 +126,36 @@ impl From<i32> for Error { #[stable(feature = "rust1", since = "1.0.0")] impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!( - f, - "{}", - match self { - Error::NoError => "no error occurred", - Error::BadAlignment => "memory was not properly aligned", - Error::BadAddress => "an invalid address was supplied", - Error::OutOfMemory => "the process or service has run out of memory", - Error::MemoryInUse => "the requested address is in use", - Error::InterruptNotFound => - "the requested interrupt does not exist on this platform", - Error::InterruptInUse => "the requested interrupt is currently in use", - Error::InvalidString => "the specified string was not formatted correctly", - Error::ServerExists => "a server with that address already exists", - Error::ServerNotFound => "the requetsed server could not be found", - Error::ProcessNotFound => "the target process does not exist", - Error::ProcessNotChild => - "the requested operation can only be done on child processes", - Error::ProcessTerminated => "the target process has crashed", - Error::Timeout => "the requested operation timed out", - Error::InternalError => "an internal error occurred", - Error::ServerQueueFull => "the server has too many pending messages", - Error::ThreadNotAvailable => "the specified thread does not exist", - Error::UnhandledSyscall => "the kernel did not recognize that syscall", - Error::InvalidSyscall => "the syscall had incorrect parameters", - Error::ShareViolation => "an attempt was made to share memory twice", - Error::InvalidThread => "tried to resume a thread that was not ready", - Error::InvalidPid => "kernel attempted to use a pid that was not valid", - Error::AccessDenied => "no permission to perform the requested operation", - Error::UseBeforeInit => "attempt to use a service before initialization finished", - Error::DoubleFree => "the requested resource was freed twice", - Error::DebugInProgress => "kernel attempted to activate a thread being debugged", - Error::InvalidLimit => "process attempted to adjust an invalid limit", - Error::UnknownError => "an unknown error occurred", - } - ) + write!(f, "{}", match self { + Error::NoError => "no error occurred", + Error::BadAlignment => "memory was not properly aligned", + Error::BadAddress => "an invalid address was supplied", + Error::OutOfMemory => "the process or service has run out of memory", + Error::MemoryInUse => "the requested address is in use", + Error::InterruptNotFound => "the requested interrupt does not exist on this platform", + Error::InterruptInUse => "the requested interrupt is currently in use", + Error::InvalidString => "the specified string was not formatted correctly", + Error::ServerExists => "a server with that address already exists", + Error::ServerNotFound => "the requetsed server could not be found", + Error::ProcessNotFound => "the target process does not exist", + Error::ProcessNotChild => "the requested operation can only be done on child processes", + Error::ProcessTerminated => "the target process has crashed", + Error::Timeout => "the requested operation timed out", + Error::InternalError => "an internal error occurred", + Error::ServerQueueFull => "the server has too many pending messages", + Error::ThreadNotAvailable => "the specified thread does not exist", + Error::UnhandledSyscall => "the kernel did not recognize that syscall", + Error::InvalidSyscall => "the syscall had incorrect parameters", + Error::ShareViolation => "an attempt was made to share memory twice", + Error::InvalidThread => "tried to resume a thread that was not ready", + Error::InvalidPid => "kernel attempted to use a pid that was not valid", + Error::AccessDenied => "no permission to perform the requested operation", + Error::UseBeforeInit => "attempt to use a service before initialization finished", + Error::DoubleFree => "the requested resource was freed twice", + Error::DebugInProgress => "kernel attempted to activate a thread being debugged", + Error::InvalidLimit => "process attempted to adjust an invalid limit", + Error::UnknownError => "an unknown error occurred", + }) } } diff --git a/library/std/src/os/xous/services.rs b/library/std/src/os/xous/services.rs index ddf0236f5ad..93916750c05 100644 --- a/library/std/src/os/xous/services.rs +++ b/library/std/src/os/xous/services.rs @@ -19,7 +19,7 @@ pub(crate) use ticktimer::*; mod ns { const NAME_MAX_LENGTH: usize = 64; - use crate::os::xous::ffi::{lend_mut, Connection}; + use crate::os::xous::ffi::{Connection, lend_mut}; // By making this repr(C), the layout of this struct becomes well-defined // and no longer shifts around. // By marking it as `align(4096)` we define that it will be page-aligned, diff --git a/library/std/src/os/xous/services/systime.rs b/library/std/src/os/xous/services/systime.rs index 079ede7aa86..de87694b4cd 100644 --- a/library/std/src/os/xous/services/systime.rs +++ b/library/std/src/os/xous/services/systime.rs @@ -1,6 +1,6 @@ use core::sync::atomic::{AtomicU32, Ordering}; -use crate::os::xous::ffi::{connect, Connection}; +use crate::os::xous::ffi::{Connection, connect}; pub(crate) enum SystimeScalar { GetUtcTimeMs, diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 6f0952c41ed..015cab89485 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -231,11 +231,11 @@ pub macro panic_2015 { }), } +#[stable(feature = "panic_hooks", since = "1.10.0")] +pub use core::panic::Location; #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] pub use core::panic::panic_2021; -#[stable(feature = "panic_hooks", since = "1.10.0")] -pub use core::panic::Location; #[stable(feature = "catch_unwind", since = "1.9.0")] pub use core::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; @@ -283,6 +283,9 @@ where { } +#[unstable(feature = "abort_unwind", issue = "130338")] +pub use core::panic::abort_unwind; + /// Invokes a closure, capturing the cause of an unwinding panic if one occurs. /// /// This function will return `Ok` with the closure's result if the closure diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index e818b448270..336e34d7b95 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -23,8 +23,8 @@ use crate::mem::{self, ManuallyDrop}; use crate::panic::{BacktraceStyle, PanicHookInfo}; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{PoisonError, RwLock}; -use crate::sys::backtrace; use crate::sys::stdio::panic_output; +use crate::sys::{backtrace, dbg}; use crate::{fmt, intrinsics, process, thread}; // Binary interface to the panic runtime that the standard library depends on. @@ -231,6 +231,7 @@ where } /// The default panic handler. +#[optimize(size)] fn default_hook(info: &PanicHookInfo<'_>) { // If this is a double panic, make sure that we print a backtrace // for this panic. Otherwise only print it if logging is enabled. @@ -249,7 +250,8 @@ fn default_hook(info: &PanicHookInfo<'_>) { let thread = thread::try_current(); let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>"); - let write = |err: &mut dyn crate::io::Write| { + let write = #[optimize(size)] + |err: &mut dyn crate::io::Write| { // Use a lock to prevent mixed output in multithreading context. // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows. let mut lock = backtrace::lock(); @@ -275,7 +277,7 @@ fn default_hook(info: &PanicHookInfo<'_>) { if cfg!(miri) { let _ = writeln!( err, - "note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` \ + "note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` \ for the environment variable to have an effect" ); } @@ -527,6 +529,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> // optimizer (in most cases this function is not inlined even as a normal, // non-cold function, though, as of the writing of this comment). #[cold] + #[optimize(size)] unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send + 'static> { // SAFETY: The whole unsafe block hinges on a correct implementation of // the panic handler `__rust_panic_cleanup`. As such we can only @@ -686,7 +689,7 @@ pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { // lang item for CTFE panic support // never inline unless panic_immediate_abort to avoid code // bloat at the call sites as much as possible -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] #[rustc_do_not_const_check] // hooked by const-eval @@ -756,6 +759,7 @@ fn payload_as_str(payload: &dyn Any) -> &str { /// Executes the primary logic for a panic, including checking for recursive /// panics, panic hooks, and finally dispatching to the panic runtime to either /// abort or unwind. +#[optimize(size)] fn rust_panic_with_hook( payload: &mut dyn PanicPayload, location: &Location<'_>, @@ -855,6 +859,14 @@ pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! { #[cfg_attr(not(test), rustc_std_internal_symbol)] #[cfg(not(feature = "panic_immediate_abort"))] fn rust_panic(msg: &mut dyn PanicPayload) -> ! { + // Break into the debugger if it is attached. + // The return value is not currently used. + // + // This function isn't used anywhere else, and + // using inside `#[panic_handler]` doesn't seem + // to count, so a warning is issued. + let _ = dbg::breakpoint_if_debugging(); + let code = unsafe { __rust_start_panic(msg) }; rtabort!("failed to initiate panic, error {code}") } @@ -862,6 +874,14 @@ fn rust_panic(msg: &mut dyn PanicPayload) -> ! { #[cfg_attr(not(test), rustc_std_internal_symbol)] #[cfg(feature = "panic_immediate_abort")] fn rust_panic(_: &mut dyn PanicPayload) -> ! { + // Break into the debugger if it is attached. + // The return value is not currently used. + // + // This function isn't used anywhere else, and + // using inside `#[panic_handler]` doesn't seem + // to count, so a warning is issued. + let _ = dbg::breakpoint_if_debugging(); + unsafe { crate::intrinsics::abort(); } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 9eaa0e01c2c..e3ff7d199cc 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -75,14 +75,14 @@ use core::clone::CloneToUninit; use crate::borrow::{Borrow, Cow}; use crate::collections::TryReserveError; use crate::error::Error; -use crate::ffi::{os_str, OsStr, OsString}; +use crate::ffi::{OsStr, OsString, os_str}; use crate::hash::{Hash, Hasher}; use crate::iter::FusedIterator; use crate::ops::{self, Deref}; use crate::rc::Rc; use crate::str::FromStr; use crate::sync::Arc; -use crate::sys::path::{is_sep_byte, is_verbatim_sep, parse_prefix, MAIN_SEP_STR}; +use crate::sys::path::{MAIN_SEP_STR, is_sep_byte, is_verbatim_sep, parse_prefix}; use crate::{cmp, fmt, fs, io, sys}; //////////////////////////////////////////////////////////////////////////////// @@ -263,6 +263,7 @@ pub fn is_separator(c: char) -> bool { /// /// For example, `/` on Unix and `\` on Windows. #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "path_main_separator")] pub const MAIN_SEPARATOR: char = crate::sys::path::MAIN_SEP; /// The primary separator of path components for the current platform. @@ -1153,6 +1154,21 @@ impl FusedIterator for Ancestors<'_> {} /// ``` /// /// Which method works best depends on what kind of situation you're in. +/// +/// Note that `PathBuf` does not always sanitize arguments, for example +/// [`push`] allows paths built from strings which include separators: +/// +/// use std::path::PathBuf; +/// +/// let mut path = PathBuf::new(); +/// +/// path.push(r"C:\"); +/// path.push("windows"); +/// path.push(r"..\otherdir"); +/// path.push("system32"); +/// +/// The behaviour of `PathBuf` may be changed to a panic on such inputs +/// in the future. [`Extend::extend`] should be used to add multi-part paths. #[cfg_attr(not(test), rustc_diagnostic_item = "PathBuf")] #[stable(feature = "rust1", since = "1.0.0")] pub struct PathBuf { @@ -1211,6 +1227,7 @@ impl PathBuf { /// let p = PathBuf::from("/test"); /// assert_eq!(Path::new("/test"), p.as_path()); /// ``` + #[cfg_attr(not(test), rustc_diagnostic_item = "pathbuf_as_path")] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] @@ -1391,6 +1408,9 @@ impl PathBuf { /// `file_name`. The new path will be a sibling of the original path. /// (That is, it will have the same parent.) /// + /// The argument is not sanitized, so can include separators. This + /// behaviour may be changed to a panic in the future. + /// /// [`self.file_name`]: Path::file_name /// [`pop`]: PathBuf::pop /// @@ -1411,6 +1431,12 @@ impl PathBuf { /// /// buf.set_file_name("baz"); /// assert!(buf == PathBuf::from("/baz")); + /// + /// buf.set_file_name("../b/c.txt"); + /// assert!(buf == PathBuf::from("/../b/c.txt")); + /// + /// buf.set_file_name("baz"); + /// assert!(buf == PathBuf::from("/../b/baz")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn set_file_name<S: AsRef<OsStr>>(&mut self, file_name: S) { @@ -2200,7 +2226,7 @@ impl Path { /// Converts a `Path` to a [`Cow<str>`]. /// - /// Any non-Unicode sequences are replaced with + /// Any non-UTF-8 sequences are replaced with /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. /// /// [U+FFFD]: super::char::REPLACEMENT_CHARACTER @@ -2240,6 +2266,7 @@ impl Path { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "path_to_pathbuf")] pub fn to_path_buf(&self) -> PathBuf { PathBuf::from(self.inner.to_os_string()) } diff --git a/library/std/src/pipe.rs b/library/std/src/pipe.rs index aa4c7014fe9..891032e94a6 100644 --- a/library/std/src/pipe.rs +++ b/library/std/src/pipe.rs @@ -12,7 +12,7 @@ //! ``` use crate::io; -use crate::sys::anonymous_pipe::{pipe as pipe_inner, AnonPipe}; +use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; /// Create anonymous pipe that is close-on-exec and blocking. #[unstable(feature = "anonymous_pipe", issue = "127154")] diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 9ffdebe1b6f..c84a5c65263 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -157,7 +157,7 @@ use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::num::NonZero; use crate::path::Path; -use crate::sys::pipe::{read2, AnonPipe}; +use crate::sys::pipe::{AnonPipe, read2}; use crate::sys::process as imp; #[stable(feature = "command_access", since = "1.57.0")] pub use crate::sys_common::process::CommandEnvs; @@ -617,8 +617,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -699,8 +697,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -748,8 +744,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -786,8 +780,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -822,8 +814,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::{Command, Stdio}; /// use std::env; @@ -870,8 +860,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -900,8 +888,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -928,8 +914,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -959,8 +943,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::{Command, Stdio}; /// @@ -988,8 +970,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::{Command, Stdio}; /// @@ -1017,8 +997,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::{Command, Stdio}; /// @@ -1039,8 +1017,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -1934,10 +1910,14 @@ impl crate::error::Error for ExitStatusError {} /// to its parent under normal termination. /// /// `ExitCode` is intended to be consumed only by the standard library (via -/// [`Termination::report()`]), and intentionally does not provide accessors like -/// `PartialEq`, `Eq`, or `Hash`. Instead the standard library provides the -/// canonical `SUCCESS` and `FAILURE` exit codes as well as `From<u8> for -/// ExitCode` for constructing other arbitrary exit codes. +/// [`Termination::report()`]). For forwards compatibility with potentially +/// unusual targets, this type currently does not provide `Eq`, `Hash`, or +/// access to the raw value. This type does provide `PartialEq` for +/// comparison, but note that there may potentially be multiple failure +/// codes, some of which will _not_ compare equal to `ExitCode::FAILURE`. +/// The standard library provides the canonical `SUCCESS` and `FAILURE` +/// exit codes as well as `From<u8> for ExitCode` for constructing other +/// arbitrary exit codes. /// /// # Portability /// @@ -1976,7 +1956,7 @@ impl crate::error::Error for ExitStatusError {} /// ExitCode::SUCCESS /// } /// ``` -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] #[stable(feature = "process_exitcode", since = "1.61.0")] pub struct ExitCode(imp::ExitCode); @@ -2105,8 +2085,6 @@ impl Child { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -2129,8 +2107,6 @@ impl Child { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -2158,8 +2134,6 @@ impl Child { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -2194,8 +2168,6 @@ impl Child { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -2296,6 +2268,15 @@ impl Child { /// } /// ``` /// +/// In its current implementation, this function will execute exit handlers registered with `atexit` +/// as well as other platform-specific exit handlers (e.g. `fini` sections of ELF shared objects). +/// This means that Rust requires that all exit handlers are safe to execute at any time. In +/// particular, if an exit handler cleans up some state that might be concurrently accessed by other +/// threads, it is required that the exit handler performs suitable synchronization with those +/// threads. (The alternative to this requirement would be to not run exit handlers at all, which is +/// considered undesirable. Note that returning from `main` also calls `exit`, so making `exit` an +/// unsafe operation is not an option.) +/// /// ## Platform-specific behavior /// /// **Unix**: On Unix-like platforms, it is unlikely that all 32 bits of `exit` @@ -2389,15 +2370,11 @@ pub fn abort() -> ! { /// /// # Examples /// -/// Basic usage: -/// /// ```no_run /// use std::process; /// /// println!("My pid is {}", process::id()); /// ``` -/// -/// #[must_use] #[stable(feature = "getpid", since = "1.26.0")] pub fn id() -> u32 { diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index f8e8e0dea55..88cc95caf40 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -437,7 +437,7 @@ fn test_proc_thread_attributes() { use crate::mem; use crate::os::windows::io::AsRawHandle; use crate::os::windows::process::CommandExt; - use crate::sys::c::{CloseHandle, BOOL, HANDLE}; + use crate::sys::c::{BOOL, CloseHandle, HANDLE}; use crate::sys::cvt; #[repr(C)] diff --git a/library/std/src/random.rs b/library/std/src/random.rs new file mode 100644 index 00000000000..ecbf02eee84 --- /dev/null +++ b/library/std/src/random.rs @@ -0,0 +1,104 @@ +//! Random value generation. +//! +//! The [`Random`] trait allows generating a random value for a type using a +//! given [`RandomSource`]. + +#[unstable(feature = "random", issue = "130703")] +pub use core::random::*; + +use crate::sys::random as sys; + +/// The default random source. +/// +/// This asks the system for random data suitable for cryptographic purposes +/// such as key generation. If security is a concern, consult the platform +/// documentation below for the specific guarantees your target provides. +/// +/// The high quality of randomness provided by this source means it can be quite +/// slow on some targets. If you need a large quantity of random numbers and +/// security is not a concern, consider using an alternative random number +/// generator (potentially seeded from this one). +/// +/// # Underlying sources +/// +/// Platform | Source +/// -----------------------|--------------------------------------------------------------- +/// Linux | [`getrandom`] or [`/dev/urandom`] after polling `/dev/random` +/// Windows | [`ProcessPrng`](https://learn.microsoft.com/en-us/windows/win32/seccng/processprng) +/// Apple | `CCRandomGenerateBytes` +/// DragonFly | [`arc4random_buf`](https://man.dragonflybsd.org/?command=arc4random) +/// ESP-IDF | [`esp_fill_random`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t) +/// FreeBSD | [`arc4random_buf`](https://man.freebsd.org/cgi/man.cgi?query=arc4random) +/// Fuchsia | [`cprng_draw`](https://fuchsia.dev/reference/syscalls/cprng_draw) +/// Haiku | `arc4random_buf` +/// Illumos | [`arc4random_buf`](https://www.illumos.org/man/3C/arc4random) +/// NetBSD | [`arc4random_buf`](https://man.netbsd.org/arc4random.3) +/// OpenBSD | [`arc4random_buf`](https://man.openbsd.org/arc4random.3) +/// Solaris | [`arc4random_buf`](https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html) +/// Vita | `arc4random_buf` +/// Hermit | `read_entropy` +/// Horizon | `getrandom` shim +/// Hurd, L4Re, QNX | `/dev/urandom` +/// Redox | `/scheme/rand` +/// SGX | [`rdrand`](https://en.wikipedia.org/wiki/RDRAND) +/// SOLID | `SOLID_RNG_SampleRandomBytes` +/// TEEOS | `TEE_GenerateRandom` +/// UEFI | [`EFI_RNG_PROTOCOL`](https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol) +/// VxWorks | `randABytes` after waiting for `randSecure` to become ready +/// WASI | [`random_get`](https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-random_getbuf-pointeru8-buf_len-size---result-errno) +/// ZKVM | `sys_rand` +/// +/// Note that the sources used might change over time. +/// +/// Consult the documentation for the underlying operations on your supported +/// targets to determine whether they provide any particular desired properties, +/// such as support for reseeding on VM fork operations. +/// +/// [`getrandom`]: https://www.man7.org/linux/man-pages/man2/getrandom.2.html +/// [`/dev/urandom`]: https://www.man7.org/linux/man-pages/man4/random.4.html +#[derive(Default, Debug, Clone, Copy)] +#[unstable(feature = "random", issue = "130703")] +pub struct DefaultRandomSource; + +#[unstable(feature = "random", issue = "130703")] +impl RandomSource for DefaultRandomSource { + fn fill_bytes(&mut self, bytes: &mut [u8]) { + sys::fill_bytes(bytes) + } +} + +/// Generates a random value with the default random source. +/// +/// This is a convenience function for `T::random(&mut DefaultRandomSource)` and +/// will sample according to the same distribution as the underlying [`Random`] +/// trait implementation. +/// +/// **Warning:** Be careful when manipulating random values! The +/// [`random`](Random::random) method on integers samples them with a uniform +/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using +/// modulo operations, some of the resulting values can become more likely than +/// others. Use audited crates when in doubt. +/// +/// # Examples +/// +/// Generating a [version 4/variant 1 UUID] represented as text: +/// ``` +/// #![feature(random)] +/// +/// use std::random::random; +/// +/// let bits: u128 = random(); +/// let g1 = (bits >> 96) as u32; +/// let g2 = (bits >> 80) as u16; +/// let g3 = (0x4000 | (bits >> 64) & 0x0fff) as u16; +/// let g4 = (0x8000 | (bits >> 48) & 0x3fff) as u16; +/// let g5 = (bits & 0xffffffffffff) as u64; +/// let uuid = format!("{g1:08x}-{g2:04x}-{g3:04x}-{g4:04x}-{g5:012x}"); +/// println!("{uuid}"); +/// ``` +/// +/// [version 4/variant 1 UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random) +#[unstable(feature = "random", issue = "130703")] +pub fn random<T: Random>() -> T { + T::random(&mut DefaultRandomSource) +} diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 307a543c9d2..b6f36931ec2 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -146,7 +146,7 @@ fn lang_start_internal( rtabort!("drop of the panic payload panicked"); }); panic::catch_unwind(cleanup).map_err(rt_abort)?; - // Guard against multple threads calling `libc::exit` concurrently. + // Guard against multiple threads calling `libc::exit` concurrently. // See the documentation for `unique_thread_exit` for more information. panic::catch_unwind(|| crate::sys::exit_guard::unique_thread_exit()).map_err(rt_abort)?; ret_code diff --git a/library/std/src/sync/barrier/tests.rs b/library/std/src/sync/barrier/tests.rs index 834a3e75158..c5620cd91d8 100644 --- a/library/std/src/sync/barrier/tests.rs +++ b/library/std/src/sync/barrier/tests.rs @@ -1,4 +1,4 @@ -use crate::sync::mpsc::{channel, TryRecvError}; +use crate::sync::mpsc::{TryRecvError, channel}; use crate::sync::{Arc, Barrier}; use crate::thread; diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs index 08d46f356d9..44ffcb528d9 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/condvar.rs @@ -2,7 +2,7 @@ mod tests; use crate::fmt; -use crate::sync::{mutex, poison, LockResult, MutexGuard, PoisonError}; +use crate::sync::{LockResult, MutexGuard, PoisonError, mutex, poison}; use crate::sys::sync as sys; use crate::time::{Duration, Instant}; @@ -195,8 +195,11 @@ impl Condvar { if poisoned { Err(PoisonError::new(guard)) } else { Ok(guard) } } - /// Blocks the current thread until this condition variable receives a - /// notification and the provided condition is false. + /// Blocks the current thread until the provided condition becomes false. + /// + /// `condition` is checked immediately; if not met (returns `true`), this + /// will [`wait`] for the next notification then check again. This repeats + /// until `condition` returns `false`, in which case this function returns. /// /// This function will atomically unlock the mutex specified (represented by /// `guard`) and block the current thread. This means that any calls @@ -210,6 +213,7 @@ impl Condvar { /// poisoned when this thread re-acquires the lock. For more information, /// see information about [poisoning] on the [`Mutex`] type. /// + /// [`wait`]: Self::wait /// [`notify_one`]: Self::notify_one /// [`notify_all`]: Self::notify_all /// [poisoning]: super::Mutex#poisoning diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 953aef40e7b..b05615035d7 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -44,8 +44,6 @@ union Data<T, F> { /// /// // The `String` is built, stored in the `LazyLock`, and returned as `&String`. /// let _ = &*DEEP_THOUGHT; -/// // The `String` is retrieved from the `LazyLock` and returned as `&String`. -/// let _ = &*DEEP_THOUGHT; /// ``` /// /// Initialize fields with `LazyLock`. @@ -121,7 +119,7 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { pub fn into_inner(mut this: Self) -> Result<T, F> { let state = this.once.state(); match state { - ExclusiveState::Poisoned => panic!("LazyLock instance has previously been poisoned"), + ExclusiveState::Poisoned => panic_poisoned(), state => { let this = ManuallyDrop::new(this); let data = unsafe { ptr::read(&this.data) }.into_inner(); @@ -134,6 +132,60 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { } } + /// Forces the evaluation of this lazy value and returns a mutable reference to + /// the result. + /// + /// # Examples + /// + /// ``` + /// #![feature(lazy_get)] + /// use std::sync::LazyLock; + /// + /// let mut lazy = LazyLock::new(|| 92); + /// + /// let p = LazyLock::force_mut(&mut lazy); + /// assert_eq!(*p, 92); + /// *p = 44; + /// assert_eq!(*lazy, 44); + /// ``` + #[inline] + #[unstable(feature = "lazy_get", issue = "129333")] + pub fn force_mut(this: &mut LazyLock<T, F>) -> &mut T { + #[cold] + /// # Safety + /// May only be called when the state is `Incomplete`. + unsafe fn really_init_mut<T, F: FnOnce() -> T>(this: &mut LazyLock<T, F>) -> &mut T { + struct PoisonOnPanic<'a, T, F>(&'a mut LazyLock<T, F>); + impl<T, F> Drop for PoisonOnPanic<'_, T, F> { + #[inline] + fn drop(&mut self) { + self.0.once.set_state(ExclusiveState::Poisoned); + } + } + + // SAFETY: We always poison if the initializer panics (then we never check the data), + // or set the data on success. + let f = unsafe { ManuallyDrop::take(&mut this.data.get_mut().f) }; + // INVARIANT: Initiated from mutable reference, don't drop because we read it. + let guard = PoisonOnPanic(this); + let data = f(); + guard.0.data.get_mut().value = ManuallyDrop::new(data); + guard.0.once.set_state(ExclusiveState::Complete); + core::mem::forget(guard); + // SAFETY: We put the value there above. + unsafe { &mut this.data.get_mut().value } + } + + let state = this.once.state(); + match state { + ExclusiveState::Poisoned => panic_poisoned(), + // SAFETY: The `Once` states we completed the initialization. + ExclusiveState::Complete => unsafe { &mut this.data.get_mut().value }, + // SAFETY: The state is `Incomplete`. + ExclusiveState::Incomplete => unsafe { really_init_mut(this) }, + } + } + /// Forces the evaluation of this lazy value and returns a reference to /// result. This is equivalent to the `Deref` impl, but is explicit. /// @@ -174,13 +226,58 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { } impl<T, F> LazyLock<T, F> { - /// Gets the inner value if it has already been initialized. - fn get(&self) -> Option<&T> { - if self.once.is_completed() { + /// Returns a reference to the value if initialized, or `None` if not. + /// + /// # Examples + /// + /// ``` + /// #![feature(lazy_get)] + /// + /// use std::sync::LazyLock; + /// + /// let mut lazy = LazyLock::new(|| 92); + /// + /// assert_eq!(LazyLock::get_mut(&mut lazy), None); + /// let _ = LazyLock::force(&lazy); + /// *LazyLock::get_mut(&mut lazy).unwrap() = 44; + /// assert_eq!(*lazy, 44); + /// ``` + #[inline] + #[unstable(feature = "lazy_get", issue = "129333")] + pub fn get_mut(this: &mut LazyLock<T, F>) -> Option<&mut T> { + // `state()` does not perform an atomic load, so prefer it over `is_complete()`. + let state = this.once.state(); + match state { + // SAFETY: + // The closure has been run successfully, so `value` has been initialized. + ExclusiveState::Complete => Some(unsafe { &mut this.data.get_mut().value }), + _ => None, + } + } + + /// Returns a mutable reference to the value if initialized, or `None` if not. + /// + /// # Examples + /// + /// ``` + /// #![feature(lazy_get)] + /// + /// use std::sync::LazyLock; + /// + /// let lazy = LazyLock::new(|| 92); + /// + /// assert_eq!(LazyLock::get(&lazy), None); + /// let _ = LazyLock::force(&lazy); + /// assert_eq!(LazyLock::get(&lazy), Some(&92)); + /// ``` + #[inline] + #[unstable(feature = "lazy_get", issue = "129333")] + pub fn get(this: &LazyLock<T, F>) -> Option<&T> { + if this.once.is_completed() { // SAFETY: // The closure has been run successfully, so `value` has been initialized // and will not be modified again. - Some(unsafe { &*(*self.data.get()).value }) + Some(unsafe { &(*this.data.get()).value }) } else { None } @@ -228,7 +325,7 @@ impl<T: Default> Default for LazyLock<T> { impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_tuple("LazyLock"); - match self.get() { + match LazyLock::get(self) { Some(v) => d.field(v), None => d.field(&format_args!("<uninit>")), }; @@ -236,6 +333,12 @@ impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> { } } +#[cold] +#[inline(never)] +fn panic_poisoned() -> ! { + panic!("LazyLock instance has previously been poisoned") +} + // We never create a `&F` from a `&LazyLock<T, F>` so it is fine // to not impl `Sync` for `F`. #[stable(feature = "lazy_cell", since = "1.80.0")] diff --git a/library/std/src/sync/lazy_lock/tests.rs b/library/std/src/sync/lazy_lock/tests.rs index 8a6ab4ac4fd..94044368305 100644 --- a/library/std/src/sync/lazy_lock/tests.rs +++ b/library/std/src/sync/lazy_lock/tests.rs @@ -142,3 +142,24 @@ fn is_sync_send() { fn assert_traits<T: Send + Sync>() {} assert_traits::<LazyLock<String>>(); } + +#[test] +#[should_panic = "has previously been poisoned"] +fn lazy_force_mut_panic() { + let mut lazy = LazyLock::<String>::new(|| panic!()); + crate::panic::catch_unwind(crate::panic::AssertUnwindSafe(|| { + let _ = LazyLock::force_mut(&mut lazy); + })) + .unwrap_err(); + let _ = &*lazy; +} + +#[test] +fn lazy_force_mut() { + let s = "abc".to_owned(); + let mut lazy = LazyLock::new(move || s); + LazyLock::force_mut(&mut lazy); + let p = LazyLock::force_mut(&mut lazy); + p.clear(); + LazyLock::force_mut(&mut lazy); +} diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index d0ba8cc3b47..0fb8e669bf8 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -9,6 +9,9 @@ //! Consider the following code, operating on some global static variables: //! //! ```rust +//! // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +//! #![allow(static_mut_refs)] +//! //! static mut A: u32 = 0; //! static mut B: u32 = 0; //! static mut C: u32 = 0; @@ -158,10 +161,10 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::sync::atomic; #[unstable(feature = "exclusive_wrapper", issue = "98407")] pub use core::sync::Exclusive; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::sync::atomic; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::sync::{Arc, Weak}; @@ -178,7 +181,7 @@ pub use self::mutex::MappedMutexGuard; pub use self::mutex::{Mutex, MutexGuard}; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated)] -pub use self::once::{Once, OnceState, ONCE_INIT}; +pub use self::once::{ONCE_INIT, Once, OnceState}; #[stable(feature = "once_cell", since = "1.70.0")] pub use self::once_lock::OnceLock; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/sync/mpmc/context.rs b/library/std/src/sync/mpmc/context.rs index 8db3c9896eb..2371d32d4ea 100644 --- a/library/std/src/sync/mpmc/context.rs +++ b/library/std/src/sync/mpmc/context.rs @@ -4,8 +4,8 @@ use super::select::Selected; use super::waker::current_thread_id; use crate::cell::Cell; use crate::ptr; -use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; use crate::sync::Arc; +use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; use crate::thread::{self, Thread}; use crate::time::Instant; diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs index bbe205cad04..88a8c75f7c8 100644 --- a/library/std/src/sync/mpmc/list.rs +++ b/library/std/src/sync/mpmc/list.rs @@ -551,7 +551,7 @@ impl<T> Channel<T> { let mut head = self.head.index.load(Ordering::Acquire); // The channel may be uninitialized, so we have to swap to avoid overwriting any sender's attempts - // to initalize the first block before noticing that the receivers disconnected. Late allocations + // to initialize the first block before noticing that the receivers disconnected. Late allocations // will be deallocated by the sender in Drop. let mut block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel); diff --git a/library/std/src/sync/mpmc/waker.rs b/library/std/src/sync/mpmc/waker.rs index fb877887f9c..1895466f95d 100644 --- a/library/std/src/sync/mpmc/waker.rs +++ b/library/std/src/sync/mpmc/waker.rs @@ -3,8 +3,8 @@ use super::context::Context; use super::select::{Operation, Selected}; use crate::ptr; -use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::Mutex; +use crate::sync::atomic::{AtomicBool, Ordering}; /// Represents a thread blocked on a specific channel operation. pub(crate) struct Entry { diff --git a/library/std/src/sync/mpmc/zero.rs b/library/std/src/sync/mpmc/zero.rs index 2b82eeda3d5..2451d7b79d1 100644 --- a/library/std/src/sync/mpmc/zero.rs +++ b/library/std/src/sync/mpmc/zero.rs @@ -9,8 +9,8 @@ use super::utils::Backoff; use super::waker::Waker; use crate::cell::UnsafeCell; use crate::marker::PhantomData; -use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::Mutex; +use crate::sync::atomic::{AtomicBool, Ordering}; use crate::time::Instant; use crate::{fmt, ptr}; diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index d417034f5af..f3de1f7bf49 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -7,7 +7,7 @@ use crate::marker::PhantomData; use crate::mem::ManuallyDrop; use crate::ops::{Deref, DerefMut}; use crate::ptr::NonNull; -use crate::sync::{poison, LockResult, TryLockError, TryLockResult}; +use crate::sync::{LockResult, TryLockError, TryLockResult, poison}; use crate::sys::sync as sys; /// A mutual exclusion primitive useful for protecting shared data diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index bf595fdea2d..5a1cd7d0b8b 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -314,6 +314,16 @@ impl Once { pub(crate) fn state(&mut self) -> ExclusiveState { self.inner.state() } + + /// Sets current state of the `Once` instance. + /// + /// Since this takes a mutable reference, no initialization can currently + /// be running, so the state must be either "incomplete", "poisoned" or + /// "complete". + #[inline] + pub(crate) fn set_state(&mut self, new_state: ExclusiveState) { + self.inner.set_state(new_state); + } } #[stable(feature = "std_debug", since = "1.16.0")] diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 56cf877ddc6..be615a5a8ef 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -498,6 +498,7 @@ impl<T> OnceLock<T> { } #[cold] + #[optimize(size)] fn initialize<F, E>(&self, f: F) -> Result<(), E> where F: FnOnce() -> Result<T, E>, @@ -516,7 +517,7 @@ impl<T> OnceLock<T> { res = Err(e); // Treat the underlying `Once` as poisoned since we - // failed to initialize our value. Calls + // failed to initialize our value. p.poison(); } } diff --git a/library/std/src/sync/once_lock/tests.rs b/library/std/src/sync/once_lock/tests.rs index 176830c6748..1fff3273d20 100644 --- a/library/std/src/sync/once_lock/tests.rs +++ b/library/std/src/sync/once_lock/tests.rs @@ -1,7 +1,7 @@ +use crate::sync::OnceLock; use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::SeqCst; use crate::sync::mpsc::channel; -use crate::sync::OnceLock; use crate::{panic, thread}; fn spawn_and_wait<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> R { diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs index 84a0b36db17..39f23a14441 100644 --- a/library/std/src/sync/reentrant_lock.rs +++ b/library/std/src/sync/reentrant_lock.rs @@ -8,7 +8,7 @@ use crate::fmt; use crate::ops::Deref; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sys::sync as sys; -use crate::thread::{current_id, ThreadId}; +use crate::thread::{ThreadId, current_id}; /// A re-entrant mutual exclusion lock /// @@ -136,7 +136,7 @@ cfg_if!( // match do we read out the actual TID. // Note also that we can use relaxed atomic operations here, because // we only ever read from the tid if `tls_addr` matches the current - // TLS address. In that case, either the the tid has been set by + // TLS address. In that case, either the tid has been set by // the current thread, or by a thread that has terminated before // the current thread was created. In either case, no further // synchronization is needed (as per <https://github.com/rust-lang/miri/issues/3450>) diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index d995a16e056..143bdef736d 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -7,7 +7,7 @@ use crate::marker::PhantomData; use crate::mem::ManuallyDrop; use crate::ops::{Deref, DerefMut}; use crate::ptr::NonNull; -use crate::sync::{poison, LockResult, TryLockError, TryLockResult}; +use crate::sync::{LockResult, TryLockError, TryLockResult, poison}; use crate::sys::sync as sys; /// A reader-writer lock diff --git a/library/std/src/sys/pal/hermit/alloc.rs b/library/std/src/sys/alloc/hermit.rs index f10d5f9227e..77f8200a70a 100644 --- a/library/std/src/sys/pal/hermit/alloc.rs +++ b/library/std/src/sys/alloc/hermit.rs @@ -1,4 +1,3 @@ -use super::hermit_abi; use crate::alloc::{GlobalAlloc, Layout, System}; #[stable(feature = "alloc_system_type", since = "1.28.0")] diff --git a/library/std/src/sys/pal/common/alloc.rs b/library/std/src/sys/alloc/mod.rs index 1b465f95d1b..2c0b533a570 100644 --- a/library/std/src/sys/pal/common/alloc.rs +++ b/library/std/src/sys/alloc/mod.rs @@ -1,10 +1,18 @@ #![forbid(unsafe_op_in_unsafe_fn)] + use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::{cmp, ptr}; +use crate::ptr; // The minimum alignment guaranteed by the architecture. This value is used to // add fast paths for low alignment values. -#[cfg(any( +#[allow(dead_code)] +const MIN_ALIGN: usize = if cfg!(any( + all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")), + all(target_arch = "xtensa", target_os = "espidf"), +)) { + // The allocator on the esp-idf and zkvm platforms guarantees 4 byte alignment. + 4 +} else if cfg!(any( target_arch = "x86", target_arch = "arm", target_arch = "m68k", @@ -16,11 +24,11 @@ use crate::{cmp, ptr}; target_arch = "sparc", target_arch = "wasm32", target_arch = "hexagon", - all(target_arch = "riscv32", not(any(target_os = "espidf", target_os = "zkvm"))), - all(target_arch = "xtensa", not(target_os = "espidf")), -))] -pub const MIN_ALIGN: usize = 8; -#[cfg(any( + target_arch = "riscv32", + target_arch = "xtensa", +)) { + 8 +} else if cfg!(any( target_arch = "x86_64", target_arch = "aarch64", target_arch = "arm64ec", @@ -31,16 +39,14 @@ pub const MIN_ALIGN: usize = 8; target_arch = "sparc64", target_arch = "riscv64", target_arch = "wasm64", -))] -pub const MIN_ALIGN: usize = 16; -// The allocator on the esp-idf and zkvm platforms guarantee 4 byte alignment. -#[cfg(all(any( - all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")), - all(target_arch = "xtensa", target_os = "espidf"), -)))] -pub const MIN_ALIGN: usize = 4; +)) { + 16 +} else { + panic!("add a value for MIN_ALIGN") +}; -pub unsafe fn realloc_fallback( +#[allow(dead_code)] +unsafe fn realloc_fallback( alloc: &System, ptr: *mut u8, old_layout: Layout, @@ -52,10 +58,37 @@ pub unsafe fn realloc_fallback( let new_ptr = GlobalAlloc::alloc(alloc, new_layout); if !new_ptr.is_null() { - let size = cmp::min(old_layout.size(), new_size); + let size = usize::min(old_layout.size(), new_size); ptr::copy_nonoverlapping(ptr, new_ptr, size); GlobalAlloc::dealloc(alloc, ptr, old_layout); } + new_ptr } } + +cfg_if::cfg_if! { + if #[cfg(any( + target_family = "unix", + target_os = "wasi", + target_os = "teeos", + ))] { + mod unix; + } else if #[cfg(target_os = "windows")] { + mod windows; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod sgx; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + } else if #[cfg(target_family = "wasm")] { + mod wasm; + } else if #[cfg(target_os = "xous")] { + mod xous; + } else if #[cfg(target_os = "zkvm")] { + mod zkvm; + } +} diff --git a/library/std/src/sys/pal/sgx/alloc.rs b/library/std/src/sys/alloc/sgx.rs index f68ede9fcf0..fca9d087e5b 100644 --- a/library/std/src/sys/pal/sgx/alloc.rs +++ b/library/std/src/sys/alloc/sgx.rs @@ -1,9 +1,8 @@ -use core::sync::atomic::{AtomicBool, Ordering}; - -use super::abi::mem as sgx_mem; -use super::waitqueue::SpinMutex; use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ptr; +use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sys::pal::abi::mem as sgx_mem; +use crate::sys::pal::waitqueue::SpinMutex; // Using a SpinMutex because we never want to exit the enclave waiting for the // allocator. diff --git a/library/std/src/sys/pal/solid/alloc.rs b/library/std/src/sys/alloc/solid.rs index 4cf60ac9b2e..47cfa2eb116 100644 --- a/library/std/src/sys/pal/solid/alloc.rs +++ b/library/std/src/sys/alloc/solid.rs @@ -1,5 +1,5 @@ +use super::{MIN_ALIGN, realloc_fallback}; use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { diff --git a/library/std/src/sys/pal/uefi/alloc.rs b/library/std/src/sys/alloc/uefi.rs index 15404ac3ea6..5221876e908 100644 --- a/library/std/src/sys/pal/uefi/alloc.rs +++ b/library/std/src/sys/alloc/uefi.rs @@ -3,9 +3,9 @@ use r_efi::protocols::loaded_image; -use super::helpers; use crate::alloc::{GlobalAlloc, Layout, System}; use crate::sync::OnceLock; +use crate::sys::pal::helpers; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { diff --git a/library/std/src/sys/pal/unix/alloc.rs b/library/std/src/sys/alloc/unix.rs index 625ba5247f1..266b69cdc1e 100644 --- a/library/std/src/sys/pal/unix/alloc.rs +++ b/library/std/src/sys/alloc/unix.rs @@ -1,6 +1,6 @@ +use super::{MIN_ALIGN, realloc_fallback}; use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ptr; -use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { @@ -11,7 +11,7 @@ unsafe impl GlobalAlloc for System { // Also see <https://github.com/rust-lang/rust/issues/45955> and // <https://github.com/rust-lang/rust/issues/62251#issuecomment-507580914>. if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::malloc(layout.size()) as *mut u8 + unsafe { libc::malloc(layout.size()) as *mut u8 } } else { // `posix_memalign` returns a non-aligned value if supplied a very // large alignment on older versions of Apple's platforms (unknown @@ -25,7 +25,7 @@ unsafe impl GlobalAlloc for System { return ptr::null_mut(); } } - aligned_malloc(&layout) + unsafe { aligned_malloc(&layout) } } } @@ -33,11 +33,11 @@ unsafe impl GlobalAlloc for System { unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { // See the comment above in `alloc` for why this check looks the way it does. if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::calloc(layout.size(), 1) as *mut u8 + unsafe { libc::calloc(layout.size(), 1) as *mut u8 } } else { - let ptr = self.alloc(layout); + let ptr = unsafe { self.alloc(layout) }; if !ptr.is_null() { - ptr::write_bytes(ptr, 0, layout.size()); + unsafe { ptr::write_bytes(ptr, 0, layout.size()) }; } ptr } @@ -45,15 +45,15 @@ unsafe impl GlobalAlloc for System { #[inline] unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - libc::free(ptr as *mut libc::c_void) + unsafe { libc::free(ptr as *mut libc::c_void) } } #[inline] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { if layout.align() <= MIN_ALIGN && layout.align() <= new_size { - libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 + unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 } } else { - realloc_fallback(self, ptr, layout, new_size) + unsafe { realloc_fallback(self, ptr, layout, new_size) } } } } @@ -81,7 +81,7 @@ cfg_if::cfg_if! { // posix_memalign only has one, clear requirement: that the alignment be a multiple of // `sizeof(void*)`. Since these are all powers of 2, we can just use max. let align = layout.align().max(crate::mem::size_of::<usize>()); - let ret = libc::posix_memalign(&mut out, align, layout.size()); + let ret = unsafe { libc::posix_memalign(&mut out, align, layout.size()) }; if ret != 0 { ptr::null_mut() } else { out as *mut u8 } } } diff --git a/library/std/src/sys/pal/wasm/alloc.rs b/library/std/src/sys/alloc/wasm.rs index ef9d753d7f8..a308fafc68b 100644 --- a/library/std/src/sys/pal/wasm/alloc.rs +++ b/library/std/src/sys/alloc/wasm.rs @@ -16,6 +16,9 @@ //! The crate itself provides a global allocator which on wasm has no //! synchronization as there are no threads! +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use crate::alloc::{GlobalAlloc, Layout, System}; static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new(); diff --git a/library/std/src/sys/pal/windows/alloc.rs b/library/std/src/sys/alloc/windows.rs index 2205885687d..a77dda6e817 100644 --- a/library/std/src/sys/pal/windows/alloc.rs +++ b/library/std/src/sys/alloc/windows.rs @@ -1,11 +1,10 @@ -use core::mem::MaybeUninit; - +use super::{MIN_ALIGN, realloc_fallback}; use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ffi::c_void; +use crate::mem::MaybeUninit; use crate::ptr; use crate::sync::atomic::{AtomicPtr, Ordering}; use crate::sys::c; -use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; #[cfg(test)] mod tests; @@ -113,28 +112,28 @@ fn init_or_get_process_heap() -> c::HANDLE { extern "C" fn process_heap_init_and_alloc( _heap: MaybeUninit<c::HANDLE>, // We pass this argument to match the ABI of `HeapAlloc` flags: u32, - dwBytes: usize, + bytes: usize, ) -> *mut c_void { let heap = init_or_get_process_heap(); if core::intrinsics::unlikely(heap.is_null()) { return ptr::null_mut(); } // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`. - unsafe { HeapAlloc(heap, flags, dwBytes) } + unsafe { HeapAlloc(heap, flags, bytes) } } #[inline(never)] fn process_heap_alloc( _heap: MaybeUninit<c::HANDLE>, // We pass this argument to match the ABI of `HeapAlloc`, flags: u32, - dwBytes: usize, + bytes: usize, ) -> *mut c_void { let heap = HEAP.load(Ordering::Relaxed); if core::intrinsics::likely(!heap.is_null()) { // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`. - unsafe { HeapAlloc(heap, flags, dwBytes) } + unsafe { HeapAlloc(heap, flags, bytes) } } else { - process_heap_init_and_alloc(MaybeUninit::uninit(), flags, dwBytes) + process_heap_init_and_alloc(MaybeUninit::uninit(), flags, bytes) } } diff --git a/library/std/src/sys/pal/windows/alloc/tests.rs b/library/std/src/sys/alloc/windows/tests.rs index 674a3e1d92d..674a3e1d92d 100644 --- a/library/std/src/sys/pal/windows/alloc/tests.rs +++ b/library/std/src/sys/alloc/windows/tests.rs diff --git a/library/std/src/sys/pal/xous/alloc.rs b/library/std/src/sys/alloc/xous.rs index 9ea43445d02..9ea43445d02 100644 --- a/library/std/src/sys/pal/xous/alloc.rs +++ b/library/std/src/sys/alloc/xous.rs diff --git a/library/std/src/sys/pal/zkvm/alloc.rs b/library/std/src/sys/alloc/zkvm.rs index 2fdca223524..a600cfa2220 100644 --- a/library/std/src/sys/pal/zkvm/alloc.rs +++ b/library/std/src/sys/alloc/zkvm.rs @@ -1,5 +1,5 @@ -use super::abi; use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::sys::pal::abi; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { diff --git a/library/std/src/sys/dbg.rs b/library/std/src/sys/dbg.rs new file mode 100644 index 00000000000..383ed84cfa2 --- /dev/null +++ b/library/std/src/sys/dbg.rs @@ -0,0 +1,229 @@ +//! Debugging aids. + +/// Presence of a debugger. The debugger being concerned +/// is expected to use the OS API to debug this process. +#[derive(Copy, Clone, Debug)] +#[allow(unused)] +pub(crate) enum DebuggerPresence { + /// The debugger is attached to this process. + Detected, + /// The debugger is not attached to this process. + NotDetected, +} + +#[cfg(target_os = "windows")] +mod os { + use super::DebuggerPresence; + + #[link(name = "kernel32")] + extern "system" { + fn IsDebuggerPresent() -> i32; + } + + pub(super) fn is_debugger_present() -> Option<DebuggerPresence> { + // SAFETY: No state is shared between threads. The call reads + // a field from the Thread Environment Block using the OS API + // as required by the documentation. + if unsafe { IsDebuggerPresent() } != 0 { + Some(DebuggerPresence::Detected) + } else { + Some(DebuggerPresence::NotDetected) + } + } +} + +#[cfg(any(target_vendor = "apple", target_os = "freebsd"))] +mod os { + use libc::{CTL_KERN, KERN_PROC, KERN_PROC_PID, c_int, sysctl}; + + use super::DebuggerPresence; + use crate::io::{Cursor, Read, Seek, SeekFrom}; + use crate::process; + + const P_TRACED: i32 = 0x00000800; + + // The assumption is that the kernel structures available to the + // user space may not shrink or repurpose the existing fields over + // time. The kernels normally adhere to that for the backward + // compatibility of the user space. + + // The macOS 14.5 SDK comes with a header `MacOSX14.5.sdk/usr/include/sys/sysctl.h` + // that defines `struct kinfo_proc` be of `648` bytes on the 64-bit system. That has + // not changed since macOS 10.13 (released in 2017) at least, validated by building + // a C program in XCode while changing the build target. Apple provides this example + // for reference: https://developer.apple.com/library/archive/qa/qa1361/_index.html. + #[cfg(target_vendor = "apple")] + const KINFO_PROC_SIZE: usize = if cfg!(target_pointer_width = "64") { 648 } else { 492 }; + #[cfg(target_vendor = "apple")] + const KINFO_PROC_FLAGS_OFFSET: u64 = if cfg!(target_pointer_width = "64") { 32 } else { 16 }; + + // Works for FreeBSD stable (13.3, 13.4) and current (14.0, 14.1). + // The size of the structure has stayed the same for a long time, + // at least since 2005: + // https://lists.freebsd.org/pipermail/freebsd-stable/2005-November/019899.html + #[cfg(target_os = "freebsd")] + const KINFO_PROC_SIZE: usize = if cfg!(target_pointer_width = "64") { 1088 } else { 768 }; + #[cfg(target_os = "freebsd")] + const KINFO_PROC_FLAGS_OFFSET: u64 = if cfg!(target_pointer_width = "64") { 368 } else { 296 }; + + pub(super) fn is_debugger_present() -> Option<DebuggerPresence> { + debug_assert_ne!(KINFO_PROC_SIZE, 0); + + let mut flags = [0u8; 4]; // `ki_flag` under FreeBSD and `p_flag` under macOS. + let mut mib = [CTL_KERN, KERN_PROC, KERN_PROC_PID, process::id() as c_int]; + let mut info_size = KINFO_PROC_SIZE; + let mut kinfo_proc = [0u8; KINFO_PROC_SIZE]; + + // SAFETY: No state is shared with other threads. The sysctl call + // is safe according to the documentation. + if unsafe { + sysctl( + mib.as_mut_ptr(), + mib.len() as u32, + kinfo_proc.as_mut_ptr().cast(), + &mut info_size, + core::ptr::null_mut(), + 0, + ) + } != 0 + { + return None; + } + debug_assert_eq!(info_size, KINFO_PROC_SIZE); + + let mut reader = Cursor::new(kinfo_proc); + reader.seek(SeekFrom::Start(KINFO_PROC_FLAGS_OFFSET)).ok()?; + reader.read_exact(&mut flags).ok()?; + // Just in case, not limiting this to the little-endian systems. + let flags = i32::from_ne_bytes(flags); + + if flags & P_TRACED != 0 { + Some(DebuggerPresence::Detected) + } else { + Some(DebuggerPresence::NotDetected) + } + } +} + +#[cfg(target_os = "linux")] +mod os { + use super::DebuggerPresence; + use crate::fs::File; + use crate::io::Read; + + pub(super) fn is_debugger_present() -> Option<DebuggerPresence> { + // This function is crafted with the following goals: + // * Memory efficiency: It avoids crashing the panicking process due to + // out-of-memory (OOM) conditions by not using large heap buffers or + // allocating significant stack space, which could lead to stack overflow. + // * Minimal binary size: The function uses a minimal set of facilities + // from the standard library to avoid increasing the resulting binary size. + // + // To achieve these goals, the function does not use `[std::io::BufReader]` + // and instead reads the file byte by byte using a sliding window approach. + // It's important to note that the "/proc/self/status" pseudo-file is synthesized + // by the Virtual File System (VFS), meaning it is not read from a slow or + // non-volatile storage medium so buffering might not be as beneficial because + // all data is read from memory, though this approach does incur a syscall for + // each byte read. + // + // We cannot make assumptions about the file size or the position of the + // target prefix ("TracerPid:"), so the function does not use + // `[std::fs::read_to_string]` thus not employing UTF-8 to ASCII checking, + // conversion, or parsing as we're looking for an ASCII prefix. + // + // These condiderations make the function deviate from the familiar concise pattern + // of searching for a string in a text file. + + fn read_byte(file: &mut File) -> Option<u8> { + let mut buffer = [0]; + file.read_exact(&mut buffer).ok()?; + Some(buffer[0]) + } + + // The ASCII prefix of the datum we're interested in. + const TRACER_PID: &[u8] = b"TracerPid:\t"; + + let mut file = File::open("/proc/self/status").ok()?; + let mut matched = 0; + + // Look for the `TRACER_PID` prefix. + while let Some(byte) = read_byte(&mut file) { + if byte == TRACER_PID[matched] { + matched += 1; + if matched == TRACER_PID.len() { + break; + } + } else { + matched = 0; + } + } + + // Was the prefix found? + if matched != TRACER_PID.len() { + return None; + } + + // It was; get the ASCII representation of the first digit + // of the PID. That is enough to see if there is a debugger + // attached as the kernel does not pad the PID on the left + // with the leading zeroes. + let byte = read_byte(&mut file)?; + if byte.is_ascii_digit() && byte != b'0' { + Some(DebuggerPresence::Detected) + } else { + Some(DebuggerPresence::NotDetected) + } + } +} + +#[cfg(not(any( + target_os = "windows", + target_vendor = "apple", + target_os = "freebsd", + target_os = "linux" +)))] +mod os { + pub(super) fn is_debugger_present() -> Option<super::DebuggerPresence> { + None + } +} + +/// Detect the debugger presence. +/// +/// The code does not try to detect the debugger at all costs (e.g., when anti-debugger +/// tricks are at play), it relies on the interfaces provided by the OS. +/// +/// Return value: +/// * `None`: it's not possible to conclude whether the debugger is attached to this +/// process or not. When checking for the presence of the debugger, the detection logic +/// encountered an issue, such as the OS API throwing an error or the feature not being +/// implemented. +/// * `Some(DebuggerPresence::Detected)`: yes, the debugger is attached +/// to this process. +/// * `Some(DebuggerPresence::NotDetected)`: no, the debugger is not +/// attached to this process. +pub(crate) fn is_debugger_present() -> Option<DebuggerPresence> { + if cfg!(miri) { None } else { os::is_debugger_present() } +} + +/// Execute the breakpoint instruction if the debugger presence is detected. +/// Useful for breaking into the debugger without the need to set a breakpoint +/// in the debugger. +/// +/// Note that there is a race between attaching or detaching the debugger, and running the +/// breakpoint instruction. This is nonetheless memory-safe, like [`crate::process::abort`] +/// is. In case the debugger is attached and the function is about +/// to run the breakpoint instruction yet right before that the debugger detaches, the +/// process will crash due to running the breakpoint instruction and the debugger not +/// handling the trap exception. +pub(crate) fn breakpoint_if_debugging() -> Option<DebuggerPresence> { + let debugger_present = is_debugger_present(); + if let Some(DebuggerPresence::Detected) = debugger_present { + // SAFETY: Executing the breakpoint instruction. No state is shared + // or modified by this code. + unsafe { core::intrinsics::breakpoint() }; + } + + debugger_present +} diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index a86b3628f24..df25b84fbbe 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -5,14 +5,17 @@ /// descriptors. mod pal; +mod alloc; mod personality; pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; +pub mod dbg; pub mod exit_guard; pub mod os_str; pub mod path; +pub mod random; pub mod sync; pub mod thread_local; diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index 433237aa6e7..6fbbec7a945 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -7,7 +7,7 @@ use crate::borrow::Cow; use crate::collections::TryReserveError; use crate::rc::Rc; use crate::sync::Arc; -use crate::sys_common::wtf8::{check_utf8_boundary, Wtf8, Wtf8Buf}; +use crate::sys_common::wtf8::{Wtf8, Wtf8Buf, check_utf8_boundary}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fmt, mem}; diff --git a/library/std/src/sys/pal/common/mod.rs b/library/std/src/sys/pal/common/mod.rs index 29fc0835d76..9af4dee401c 100644 --- a/library/std/src/sys/pal/common/mod.rs +++ b/library/std/src/sys/pal/common/mod.rs @@ -10,7 +10,6 @@ #![allow(dead_code)] -pub mod alloc; pub mod small_c_string; #[cfg(test)] diff --git a/library/std/src/sys/pal/hermit/args.rs b/library/std/src/sys/pal/hermit/args.rs index 51afe3434ae..44024260277 100644 --- a/library/std/src/sys/pal/hermit/args.rs +++ b/library/std/src/sys/pal/hermit/args.rs @@ -1,4 +1,4 @@ -use crate::ffi::{c_char, CStr, OsString}; +use crate::ffi::{CStr, OsString, c_char}; use crate::os::hermit::ffi::OsStringExt; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sync::atomic::{AtomicIsize, AtomicPtr}; diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index aaf1a044d06..70f6981f717 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -1,7 +1,7 @@ use super::fd::FileDesc; use super::hermit_abi::{ - self, dirent64, stat as stat_struct, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, - O_DIRECTORY, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, + self, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, O_DIRECTORY, O_EXCL, O_RDONLY, + O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, dirent64, stat as stat_struct, }; use crate::ffi::{CStr, OsStr, OsString}; use crate::io::{self, BorrowedCursor, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index ef406b9ec7f..f49ef947174 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -18,7 +18,6 @@ use crate::os::raw::c_char; -pub mod alloc; pub mod args; pub mod env; pub mod fd; @@ -53,20 +52,6 @@ pub fn abort_internal() -> ! { unsafe { hermit_abi::abort() } } -pub fn hashmap_random_keys() -> (u64, u64) { - let mut buf = [0; 16]; - let mut slice = &mut buf[..]; - while !slice.is_empty() { - let res = cvt(unsafe { hermit_abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) }) - .expect("failed to generate random hashmap keys"); - slice = &mut slice[res as usize..]; - } - - let key1 = buf[..8].try_into().unwrap(); - let key2 = buf[8..].try_into().unwrap(); - (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) -} - // This function is needed by the panic runtime. The symbol is named in // pre-link args for the target specification, so keep that in sync. #[cfg(not(test))] diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index 6321f92e3d9..4c0c0919f47 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -77,8 +77,11 @@ impl Thread { #[inline] pub fn sleep(dur: Duration) { + let micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; + let micros = u64::try_from(micros).unwrap_or(u64::MAX); + unsafe { - hermit_abi::usleep(dur.as_micros() as u64); + hermit_abi::usleep(micros); } } diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index 2c87c4860a2..99166b15602 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -2,7 +2,7 @@ use core::hash::{Hash, Hasher}; -use super::hermit_abi::{self, timespec, CLOCK_MONOTONIC, CLOCK_REALTIME}; +use super::hermit_abi::{self, CLOCK_MONOTONIC, CLOCK_REALTIME, timespec}; use crate::cmp::Ordering; use crate::ops::{Add, AddAssign, Sub, SubAssign}; use crate::time::Duration; diff --git a/library/std/src/sys/pal/itron/task.rs b/library/std/src/sys/pal/itron/task.rs index 5da0c591788..49c420baca2 100644 --- a/library/std/src/sys/pal/itron/task.rs +++ b/library/std/src/sys/pal/itron/task.rs @@ -1,5 +1,5 @@ use super::abi; -use super::error::{fail, fail_aborting, ItronError}; +use super::error::{ItronError, fail, fail_aborting}; use crate::mem::MaybeUninit; /// Gets the ID of the task in Running state. Panics on failure. diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index 01e69afa99e..04095e1a7cf 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -1,7 +1,7 @@ //! Thread implementation backed by μITRON tasks. Assumes `acre_tsk` and //! `exd_tsk` are available. -use super::error::{expect_success, expect_success_aborting, ItronError}; +use super::error::{ItronError, expect_success, expect_success_aborting}; use super::time::dur2reltims; use super::{abi, task}; use crate::cell::UnsafeCell; diff --git a/library/std/src/sys/pal/itron/time/tests.rs b/library/std/src/sys/pal/itron/time/tests.rs index d14035d9da4..28db4f8b679 100644 --- a/library/std/src/sys/pal/itron/time/tests.rs +++ b/library/std/src/sys/pal/itron/time/tests.rs @@ -8,26 +8,24 @@ fn reltim2dur(t: u64) -> Duration { fn test_dur2reltims() { assert_eq!(dur2reltims(reltim2dur(0)).collect::<Vec<_>>(), vec![]); assert_eq!(dur2reltims(reltim2dur(42)).collect::<Vec<_>>(), vec![42]); - assert_eq!( - dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(), - vec![abi::TMAX_RELTIM] - ); - assert_eq!( - dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(), - vec![abi::TMAX_RELTIM, 10000] - ); + assert_eq!(dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(), vec![ + abi::TMAX_RELTIM + ]); + assert_eq!(dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(), vec![ + abi::TMAX_RELTIM, + 10000 + ]); } #[test] fn test_dur2tmos() { assert_eq!(dur2tmos(reltim2dur(0)).collect::<Vec<_>>(), vec![0]); assert_eq!(dur2tmos(reltim2dur(42)).collect::<Vec<_>>(), vec![42]); - assert_eq!( - dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(), - vec![abi::TMAX_RELTIM] - ); - assert_eq!( - dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(), - vec![abi::TMAX_RELTIM, 10000] - ); + assert_eq!(dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(), vec![ + abi::TMAX_RELTIM + ]); + assert_eq!(dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(), vec![ + abi::TMAX_RELTIM, + 10000 + ]); } diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs index def1ccdf81a..90b9b59471a 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs @@ -1,6 +1,6 @@ use crate::cmp; use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult}; -use crate::sys::rand::rdrand64; +use crate::random::{DefaultRandomSource, Random}; use crate::time::{Duration, Instant}; pub(crate) mod alloc; @@ -164,7 +164,7 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> { // trusted to ensure accurate timeouts. if let Ok(timeout_signed) = i64::try_from(timeout) { let tenth = timeout_signed / 10; - let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0); + let deviation = i64::random(&mut DefaultRandomSource).checked_rem(tenth).unwrap_or(0); timeout = timeout_signed.saturating_add(deviation) as _; } } diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/tests.rs b/library/std/src/sys/pal/sgx/abi/usercalls/tests.rs index ef824d35f4a..5978ae5a0fa 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/tests.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/tests.rs @@ -1,4 +1,4 @@ -use super::alloc::{copy_from_userspace, copy_to_userspace, User}; +use super::alloc::{User, copy_from_userspace, copy_to_userspace}; #[test] fn test_copy_to_userspace_function() { diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 851ab9b9f97..586ccd18c2f 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -9,7 +9,6 @@ use crate::io::ErrorKind; use crate::sync::atomic::{AtomicBool, Ordering}; pub mod abi; -pub mod alloc; pub mod args; pub mod env; pub mod fd; @@ -133,24 +132,6 @@ pub extern "C" fn __rust_abort() { abort_internal(); } -pub mod rand { - pub fn rdrand64() -> u64 { - unsafe { - let mut ret: u64 = 0; - for _ in 0..10 { - if crate::arch::x86_64::_rdrand64_step(&mut ret) == 1 { - return ret; - } - } - rtabort!("Failed to obtain random data"); - } - } -} - -pub fn hashmap_random_keys() -> (u64, u64) { - (self::rand::rdrand64(), self::rand::rdrand64()) -} - pub use crate::sys_common::{AsInner, FromInner, IntoInner}; pub trait TryIntoInner<Inner>: Sized { diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/pal/sgx/net.rs index f2e751c5119..44913ffe3a9 100644 --- a/library/std/src/sys/pal/sgx/net.rs +++ b/library/std/src/sys/pal/sgx/net.rs @@ -3,7 +3,7 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sync::Arc; use crate::sys::fd::FileDesc; -use crate::sys::{sgx_ineffective, unsupported, AsInner, FromInner, IntoInner, TryIntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported}; use crate::time::Duration; use crate::{error, fmt}; diff --git a/library/std/src/sys/pal/sgx/waitqueue/mod.rs b/library/std/src/sys/pal/sgx/waitqueue/mod.rs index bd114523fe8..41d1413fcde 100644 --- a/library/std/src/sys/pal/sgx/waitqueue/mod.rs +++ b/library/std/src/sys/pal/sgx/waitqueue/mod.rs @@ -16,9 +16,9 @@ mod tests; mod spin_mutex; mod unsafe_list; -use fortanix_sgx_abi::{Tcs, EV_UNPARK, WAIT_INDEFINITE}; +use fortanix_sgx_abi::{EV_UNPARK, Tcs, WAIT_INDEFINITE}; -pub use self::spin_mutex::{try_lock_or_false, SpinMutex, SpinMutexGuard}; +pub use self::spin_mutex::{SpinMutex, SpinMutexGuard, try_lock_or_false}; use self::unsafe_list::{UnsafeList, UnsafeListEntry}; use super::abi::{thread, usercalls}; use crate::num::NonZero; diff --git a/library/std/src/sys/pal/solid/abi/fs.rs b/library/std/src/sys/pal/solid/abi/fs.rs index 394be15b006..6864a3e7745 100644 --- a/library/std/src/sys/pal/solid/abi/fs.rs +++ b/library/std/src/sys/pal/solid/abi/fs.rs @@ -1,8 +1,8 @@ //! `solid_fs.h` pub use libc::{ - ino_t, off_t, stat, time_t, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, - SEEK_CUR, SEEK_END, SEEK_SET, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFMT, S_IFREG, S_IWRITE, + O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFBLK, S_IFCHR, S_IFDIR, + S_IFIFO, S_IFMT, S_IFREG, S_IWRITE, SEEK_CUR, SEEK_END, SEEK_SET, ino_t, off_t, stat, time_t, }; use crate::os::raw::{c_char, c_int, c_uchar}; diff --git a/library/std/src/sys/pal/solid/abi/mod.rs b/library/std/src/sys/pal/solid/abi/mod.rs index 62cd86236bb..4d097057217 100644 --- a/library/std/src/sys/pal/solid/abi/mod.rs +++ b/library/std/src/sys/pal/solid/abi/mod.rs @@ -4,7 +4,7 @@ mod fs; pub mod sockets; pub use self::fs::*; // `solid_types.h` -pub use super::itron::abi::{ER, ER_ID, E_TMOUT, ID}; +pub use super::itron::abi::{E_TMOUT, ER, ER_ID, ID}; pub const SOLID_ERR_NOTFOUND: ER = -1000; pub const SOLID_ERR_NOTSUPPORTED: ER = -1001; diff --git a/library/std/src/sys/pal/solid/error.rs b/library/std/src/sys/pal/solid/error.rs index 66c4d8a0ea2..e092497856d 100644 --- a/library/std/src/sys/pal/solid/error.rs +++ b/library/std/src/sys/pal/solid/error.rs @@ -1,4 +1,4 @@ -pub use self::itron::error::{expect_success, ItronError as SolidError}; +pub use self::itron::error::{ItronError as SolidError, expect_success}; use super::{abi, itron, net}; use crate::io::ErrorKind; diff --git a/library/std/src/sys/pal/solid/fs.rs b/library/std/src/sys/pal/solid/fs.rs index 591be66fcb4..bce9aa6d99c 100644 --- a/library/std/src/sys/pal/solid/fs.rs +++ b/library/std/src/sys/pal/solid/fs.rs @@ -538,7 +538,7 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> { } }; // ignore internal NotFound errors - if let Err(err) = result + if let Err(err) = &result && err.kind() != io::ErrorKind::NotFound { return result; diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index cbf34286878..d41042be518 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -16,7 +16,6 @@ pub mod itron { use super::unsupported; } -pub mod alloc; #[path = "../unsupported/args.rs"] pub mod args; pub mod env; @@ -63,13 +62,3 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { pub fn abort_internal() -> ! { unsafe { libc::abort() } } - -pub fn hashmap_random_keys() -> (u64, u64) { - unsafe { - let mut out = crate::mem::MaybeUninit::<[u64; 2]>::uninit(); - let result = abi::SOLID_RNG_SampleRandomBytes(out.as_mut_ptr() as *mut u8, 16); - assert_eq!(result, 0, "SOLID_RNG_SampleRandomBytes failed: {result}"); - let [x1, x2] = out.assume_init(); - (x1, x2) - } -} diff --git a/library/std/src/sys/pal/solid/net.rs b/library/std/src/sys/pal/solid/net.rs index b6a31395095..c0818ecd856 100644 --- a/library/std/src/sys/pal/solid/net.rs +++ b/library/std/src/sys/pal/solid/net.rs @@ -1,6 +1,6 @@ use libc::{c_int, c_void, size_t}; -use self::netc::{sockaddr, socklen_t, MSG_PEEK}; +use self::netc::{MSG_PEEK, sockaddr, socklen_t}; use super::abi; use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; diff --git a/library/std/src/sys/pal/teeos/alloc.rs b/library/std/src/sys/pal/teeos/alloc.rs deleted file mode 100644 index b280d1dd76f..00000000000 --- a/library/std/src/sys/pal/teeos/alloc.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::ptr; -use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; - -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // jemalloc provides alignment less than MIN_ALIGN for small allocations. - // So only rely on MIN_ALIGN if size >= align. - // Also see <https://github.com/rust-lang/rust/issues/45955> and - // <https://github.com/rust-lang/rust/issues/62251#issuecomment-507580914>. - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - unsafe { libc::malloc(layout.size()) as *mut u8 } - } else { - unsafe { aligned_malloc(&layout) } - } - } - - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // See the comment above in `alloc` for why this check looks the way it does. - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - unsafe { libc::calloc(layout.size(), 1) as *mut u8 } - } else { - let ptr = unsafe { self.alloc(layout) }; - if !ptr.is_null() { - unsafe { ptr::write_bytes(ptr, 0, layout.size()) }; - } - ptr - } - } - - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - unsafe { libc::free(ptr as *mut libc::c_void) } - } - - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - if layout.align() <= MIN_ALIGN && layout.align() <= new_size { - unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 } - } else { - unsafe { realloc_fallback(self, ptr, layout, new_size) } - } - } -} - -#[inline] -unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { - let mut out = ptr::null_mut(); - // posix_memalign requires that the alignment be a multiple of `sizeof(void*)`. - // Since these are all powers of 2, we can just use max. - let align = layout.align().max(crate::mem::size_of::<usize>()); - let ret = unsafe { libc::posix_memalign(&mut out, align, layout.size()) }; - if ret != 0 { ptr::null_mut() } else { out as *mut u8 } -} diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index adefd1bb42c..60a227afb84 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -6,9 +6,6 @@ #![allow(unused_variables)] #![allow(dead_code)] -pub use self::rand::hashmap_random_keys; - -pub mod alloc; #[path = "../unsupported/args.rs"] pub mod args; #[path = "../unsupported/env.rs"] @@ -24,7 +21,6 @@ pub mod os; pub mod pipe; #[path = "../unsupported/process.rs"] pub mod process; -mod rand; pub mod stdio; pub mod thread; #[allow(non_upper_case_globals)] diff --git a/library/std/src/sys/pal/teeos/rand.rs b/library/std/src/sys/pal/teeos/rand.rs deleted file mode 100644 index b45c3bb40e7..00000000000 --- a/library/std/src/sys/pal/teeos/rand.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub fn hashmap_random_keys() -> (u64, u64) { - const KEY_LEN: usize = core::mem::size_of::<u64>(); - - let mut v = [0u8; KEY_LEN * 2]; - imp::fill_bytes(&mut v); - - let key1 = v[0..KEY_LEN].try_into().unwrap(); - let key2 = v[KEY_LEN..].try_into().unwrap(); - - (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) -} - -mod imp { - extern "C" { - fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t); - } - - pub fn fill_bytes(v: &mut [u8]) { - unsafe { TEE_GenerateRandom(v.as_mut_ptr() as _, v.len() * crate::mem::size_of::<u8>()) } - } -} diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 4031d33ba80..bd8a6684b64 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -14,7 +14,7 @@ use r_efi::protocols::{device_path, device_path_to_text}; use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_io_error}; -use crate::mem::{size_of, MaybeUninit}; +use crate::mem::{MaybeUninit, size_of}; use crate::os::uefi::env::boot_services; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::os::uefi::{self}; diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 851bcea4c1e..c0ab52f650a 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -13,11 +13,11 @@ //! [`OsString`]: crate::ffi::OsString #![forbid(unsafe_op_in_unsafe_fn)] -pub mod alloc; pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; +pub mod helpers; #[path = "../unsupported/io.rs"] pub mod io; #[path = "../unsupported/net.rs"] @@ -30,8 +30,6 @@ pub mod stdio; pub mod thread; pub mod time; -mod helpers; - #[cfg(test)] mod tests; @@ -181,39 +179,6 @@ pub extern "C" fn __rust_abort() { abort_internal(); } -#[inline] -pub fn hashmap_random_keys() -> (u64, u64) { - get_random().unwrap() -} - -fn get_random() -> Option<(u64, u64)> { - use r_efi::protocols::rng; - - let mut buf = [0u8; 16]; - let handles = helpers::locate_handles(rng::PROTOCOL_GUID).ok()?; - for handle in handles { - if let Ok(protocol) = helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) { - let r = unsafe { - ((*protocol.as_ptr()).get_rng)( - protocol.as_ptr(), - crate::ptr::null_mut(), - buf.len(), - buf.as_mut_ptr(), - ) - }; - if r.is_error() { - continue; - } else { - return Some(( - u64::from_le_bytes(buf[..8].try_into().ok()?), - u64::from_le_bytes(buf[8..].try_into().ok()?), - )); - } - } - } - None -} - /// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { uefi::env::disable_boot_services(); diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 4d2d7264704..9aee67d622f 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -1,7 +1,7 @@ -use r_efi::efi::protocols::{device_path, loaded_image_device_path}; use r_efi::efi::Status; +use r_efi::efi::protocols::{device_path, loaded_image_device_path}; -use super::{helpers, unsupported, RawOsError}; +use super::{RawOsError, helpers, unsupported}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs index fdc5f5d7e4f..0cc9cecb89d 100644 --- a/library/std/src/sys/pal/uefi/process.rs +++ b/library/std/src/sys/pal/uefi/process.rs @@ -470,7 +470,7 @@ mod uefi_command_internal { let st_size = unsafe { (*self.st.as_ptr()).hdr.header_size as usize }; let mut crc32: u32 = 0; - // Set crc to 0 before calcuation + // Set crc to 0 before calculation unsafe { (*self.st.as_mut_ptr()).hdr.crc32 = 0; } diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs index 9a37e1a0346..a943e3a581a 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/pal/unix/args.rs @@ -112,6 +112,7 @@ impl DoubleEndedIterator for Args { target_os = "aix", target_os = "nto", target_os = "hurd", + target_os = "rtems", ))] mod imp { use crate::ffi::c_char; diff --git a/library/std/src/sys/pal/unix/env.rs b/library/std/src/sys/pal/unix/env.rs index fb1f868644d..b2d399b8791 100644 --- a/library/std/src/sys/pal/unix/env.rs +++ b/library/std/src/sys/pal/unix/env.rs @@ -240,6 +240,17 @@ pub mod os { pub const EXE_EXTENSION: &str = ""; } +#[cfg(target_os = "rtems")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "rtems"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + #[cfg(target_os = "vxworks")] pub mod os { pub const FAMILY: &str = "unix"; diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs index d8e239ee23e..135042779ad 100644 --- a/library/std/src/sys/pal/unix/fd.rs +++ b/library/std/src/sys/pal/unix/fd.rs @@ -3,14 +3,6 @@ #[cfg(test)] mod tests; -#[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "emscripten", - target_os = "l4re", - target_os = "hurd", -))] -use libc::off64_t; #[cfg(not(any( target_os = "linux", target_os = "emscripten", @@ -19,6 +11,14 @@ use libc::off64_t; target_os = "hurd", )))] use libc::off_t as off64_t; +#[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "emscripten", + target_os = "l4re", + target_os = "hurd", +))] +use libc::off64_t; use crate::cmp; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index fc9d7e98883..dade6d6bbae 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -31,10 +31,6 @@ use libc::fstatat64; all(target_os = "linux", target_env = "musl"), ))] use libc::readdir as readdir64; -#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] -use libc::readdir64; -#[cfg(any(target_os = "emscripten", target_os = "l4re"))] -use libc::readdir64_r; #[cfg(not(any( target_os = "android", target_os = "linux", @@ -50,6 +46,10 @@ use libc::readdir64_r; target_os = "hurd", )))] use libc::readdir_r as readdir64_r; +#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] +use libc::readdir64; +#[cfg(any(target_os = "emscripten", target_os = "l4re"))] +use libc::readdir64_r; use libc::{c_int, mode_t}; #[cfg(target_os = "android")] use libc::{ @@ -478,6 +478,7 @@ impl FileAttr { target_os = "horizon", target_os = "vita", target_os = "hurd", + target_os = "rtems", )))] pub fn modified(&self) -> io::Result<SystemTime> { #[cfg(target_pointer_width = "32")] @@ -490,7 +491,12 @@ impl FileAttr { SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64) } - #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))] + #[cfg(any( + target_os = "vxworks", + target_os = "espidf", + target_os = "vita", + target_os = "rtems", + ))] pub fn modified(&self) -> io::Result<SystemTime> { SystemTime::new(self.stat.st_mtime as i64, 0) } @@ -506,6 +512,7 @@ impl FileAttr { target_os = "horizon", target_os = "vita", target_os = "hurd", + target_os = "rtems", )))] pub fn accessed(&self) -> io::Result<SystemTime> { #[cfg(target_pointer_width = "32")] @@ -518,7 +525,12 @@ impl FileAttr { SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64) } - #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))] + #[cfg(any( + target_os = "vxworks", + target_os = "espidf", + target_os = "vita", + target_os = "rtems" + ))] pub fn accessed(&self) -> io::Result<SystemTime> { SystemTime::new(self.stat.st_atime as i64, 0) } @@ -853,6 +865,7 @@ impl Drop for Dir { target_os = "fuchsia", target_os = "horizon", target_os = "vxworks", + target_os = "rtems", )))] { let fd = unsafe { libc::dirfd(self.0) }; @@ -970,6 +983,7 @@ impl DirEntry { target_os = "aix", target_os = "nto", target_os = "hurd", + target_os = "rtems", target_vendor = "apple", ))] pub fn ino(&self) -> u64 { @@ -1717,7 +1731,7 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { run_path_with_cstr(original, &|original| { run_path_with_cstr(link, &|link| { cfg_if::cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { + if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_env = "nto70"))] { // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves // it implementation-defined whether `link` follows symlinks, so rely on the // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. @@ -1852,7 +1866,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { let max_len = u64::MAX; let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; - use super::kernel_copy::{copy_regular_files, CopyResult}; + use super::kernel_copy::{CopyResult, copy_regular_files}; match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) { CopyResult::Ended(bytes) => Ok(bytes), @@ -1994,7 +2008,7 @@ mod remove_dir_impl { #[cfg(all(target_os = "linux", target_env = "gnu"))] use libc::{fdopendir, openat64 as openat, unlinkat}; - use super::{lstat, Dir, DirEntry, InnerReadDir, ReadDir}; + use super::{Dir, DirEntry, InnerReadDir, ReadDir, lstat}; use crate::ffi::CStr; use crate::io; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}; diff --git a/library/std/src/sys/pal/unix/l4re.rs b/library/std/src/sys/pal/unix/l4re.rs index fe9559f2a56..52d39dcfb16 100644 --- a/library/std/src/sys/pal/unix/l4re.rs +++ b/library/std/src/sys/pal/unix/l4re.rs @@ -54,6 +54,10 @@ pub mod net { unimpl!(); } + pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { + unimpl!(); + } + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { unimpl!(); } diff --git a/library/std/src/sys/pal/unix/linux/pidfd.rs b/library/std/src/sys/pal/unix/linux/pidfd.rs index 7474f80e94f..78744430f3b 100644 --- a/library/std/src/sys/pal/unix/linux/pidfd.rs +++ b/library/std/src/sys/pal/unix/linux/pidfd.rs @@ -13,7 +13,7 @@ pub(crate) struct PidFd(FileDesc); impl PidFd { pub fn kill(&self) -> io::Result<()> { - return cvt(unsafe { + cvt(unsafe { libc::syscall( libc::SYS_pidfd_send_signal, self.0.as_raw_fd(), @@ -22,7 +22,7 @@ impl PidFd { 0, ) }) - .map(drop); + .map(drop) } pub fn wait(&self) -> io::Result<ExitStatus> { @@ -30,7 +30,7 @@ impl PidFd { cvt(unsafe { libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, libc::WEXITED) })?; - return Ok(ExitStatus::from_waitid_siginfo(siginfo)); + Ok(ExitStatus::from_waitid_siginfo(siginfo)) } pub fn try_wait(&self) -> io::Result<Option<ExitStatus>> { @@ -45,9 +45,10 @@ impl PidFd { ) })?; if unsafe { siginfo.si_pid() } == 0 { - return Ok(None); + Ok(None) + } else { + Ok(Some(ExitStatus::from_waitid_siginfo(siginfo))) } - return Ok(Some(ExitStatus::from_waitid_siginfo(siginfo))); } } diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 10df3306f92..1c9159e5fba 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -1,13 +1,11 @@ #![allow(missing_docs, nonstandard_style)] -pub use self::rand::hashmap_random_keys; use crate::io::ErrorKind; #[cfg(not(target_os = "espidf"))] #[macro_use] pub mod weak; -pub mod alloc; pub mod args; pub mod env; pub mod fd; @@ -27,7 +25,6 @@ pub use self::l4re::net; pub mod os; pub mod pipe; pub mod process; -pub mod rand; pub mod stack_overflow; pub mod stdio; pub mod thread; @@ -80,6 +77,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "l4re", target_os = "horizon", target_os = "vita", + target_os = "rtems", // The poll on Darwin doesn't set POLLNVAL for closed fds. target_vendor = "apple", )))] diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index bc0e3f4eeea..e98232ba89a 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/pal/unix/net.rs @@ -1,4 +1,4 @@ -use libc::{c_int, c_void, size_t, sockaddr, socklen_t, MSG_PEEK}; +use libc::{MSG_PEEK, c_int, c_void, size_t, sockaddr, socklen_t}; use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; @@ -215,7 +215,7 @@ impl Socket { _ => { if cfg!(target_os = "vxworks") { // VxWorks poll does not return POLLHUP or POLLERR in revents. Check if the - // connnection actually succeeded and return ok only when the socket is + // connection actually succeeded and return ok only when the socket is // ready and no errors were found. if let Some(e) = self.take_error()? { return Err(e); diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index a785b97ac8d..503f8915256 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -31,7 +31,7 @@ cfg_if::cfg_if! { } extern "C" { - #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))] + #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] #[cfg_attr( any( target_os = "linux", @@ -61,13 +61,14 @@ extern "C" { } /// Returns the platform-specific value of errno -#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))] +#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] pub fn errno() -> i32 { unsafe { (*errno_location()) as i32 } } /// Sets the platform-specific value of errno -#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks")))] // needed for readdir and syscall! +// needed for readdir and syscall! +#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks"), not(target_os = "rtems")))] #[allow(dead_code)] // but not all target cfgs actually end up using it pub fn set_errno(e: i32) { unsafe { *errno_location() = e as c_int } @@ -78,6 +79,16 @@ pub fn errno() -> i32 { unsafe { libc::errnoGet() } } +#[cfg(target_os = "rtems")] +pub fn errno() -> i32 { + extern "C" { + #[thread_local] + static _tls_errno: c_int; + } + + unsafe { _tls_errno as i32 } +} + #[cfg(target_os = "dragonfly")] pub fn errno() -> i32 { extern "C" { @@ -472,7 +483,7 @@ pub fn current_exe() -> io::Result<PathBuf> { } } -#[cfg(target_os = "redox")] +#[cfg(any(target_os = "redox", target_os = "rtems"))] pub fn current_exe() -> io::Result<PathBuf> { crate::fs::read_to_string("sys:exe").map(PathBuf::from) } diff --git a/library/std/src/sys/pal/unix/process/process_common.rs b/library/std/src/sys/pal/unix/process/process_common.rs index fec82505419..d9c41d43487 100644 --- a/library/std/src/sys/pal/unix/process/process_common.rs +++ b/library/std/src/sys/pal/unix/process/process_common.rs @@ -1,7 +1,7 @@ #[cfg(all(test, not(target_os = "emscripten")))] mod tests; -use libc::{c_char, c_int, gid_t, pid_t, uid_t, EXIT_FAILURE, EXIT_SUCCESS}; +use libc::{EXIT_FAILURE, EXIT_SUCCESS, c_char, c_int, gid_t, pid_t, uid_t}; use crate::collections::BTreeMap; use crate::ffi::{CStr, CString, OsStr, OsString}; diff --git a/library/std/src/sys/pal/unix/process/process_fuchsia.rs b/library/std/src/sys/pal/unix/process/process_fuchsia.rs index f3d5fdec4c2..34ff464aa37 100644 --- a/library/std/src/sys/pal/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/pal/unix/process/process_fuchsia.rs @@ -2,7 +2,7 @@ use libc::{c_int, size_t}; use crate::num::NonZero; use crate::sys::process::process_common::*; -use crate::sys::process::zircon::{zx_handle_t, Handle}; +use crate::sys::process::zircon::{Handle, zx_handle_t}; use crate::{fmt, io, mem, ptr}; //////////////////////////////////////////////////////////////////////////////// diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index 5552e9ac977..d812aa0e02c 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -19,7 +19,8 @@ use crate::sys::process::process_common::*; use crate::{fmt, mem, sys}; cfg_if::cfg_if! { - if #[cfg(all(target_os = "nto", target_env = "nto71"))] { + // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0 + if #[cfg(any(target_env = "nto70", target_env = "nto71"))] { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; use crate::time::Duration; @@ -189,7 +190,8 @@ impl Command { #[cfg(not(any( target_os = "watchos", target_os = "tvos", - all(target_os = "nto", target_env = "nto71"), + target_env = "nto70", + target_env = "nto71" )))] unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> { cvt(libc::fork()) @@ -199,7 +201,8 @@ impl Command { // or closed a file descriptor while the fork() was occurring". // Documentation says "... or try calling fork() again". This is what we do here. // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html - #[cfg(all(target_os = "nto", target_env = "nto71"))] + // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0 + #[cfg(any(target_env = "nto70", target_env = "nto71"))] unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> { use crate::sys::os::errno; @@ -537,7 +540,7 @@ impl Command { // or closed a file descriptor while the posix_spawn() was occurring". // Documentation says "... or try calling posix_spawn() again". This is what we do here. // See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html - #[cfg(all(target_os = "nto", target_env = "nto71"))] + #[cfg(target_os = "nto")] unsafe fn retrying_libc_posix_spawnp( pid: *mut pid_t, file: *const c_char, @@ -1086,13 +1089,13 @@ fn signal_string(signal: i32) -> &'static str { libc::SIGURG => " (SIGURG)", #[cfg(not(target_os = "l4re"))] libc::SIGXCPU => " (SIGXCPU)", - #[cfg(not(target_os = "l4re"))] + #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] libc::SIGXFSZ => " (SIGXFSZ)", - #[cfg(not(target_os = "l4re"))] + #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] libc::SIGVTALRM => " (SIGVTALRM)", #[cfg(not(target_os = "l4re"))] libc::SIGPROF => " (SIGPROF)", - #[cfg(not(target_os = "l4re"))] + #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] libc::SIGWINCH => " (SIGWINCH)", #[cfg(not(any(target_os = "haiku", target_os = "l4re")))] libc::SIGIO => " (SIGIO)", @@ -1187,8 +1190,8 @@ impl ExitStatusError { mod linux_child_ext { use crate::os::linux::process as os; - use crate::sys::pal::unix::linux::pidfd as imp; use crate::sys::pal::unix::ErrorKind; + use crate::sys::pal::unix::linux::pidfd as imp; use crate::sys_common::FromInner; use crate::{io, mem}; diff --git a/library/std/src/sys/pal/unix/process/process_vxworks.rs b/library/std/src/sys/pal/unix/process/process_vxworks.rs index 0477b3d9a70..2d9a304c495 100644 --- a/library/std/src/sys/pal/unix/process/process_vxworks.rs +++ b/library/std/src/sys/pal/unix/process/process_vxworks.rs @@ -1,5 +1,5 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use libc::{self, c_char, c_int, RTP_ID}; +use libc::{self, RTP_ID, c_char, c_int}; use crate::io::{self, ErrorKind}; use crate::num::NonZero; diff --git a/library/std/src/sys/pal/unix/rand.rs b/library/std/src/sys/pal/unix/rand.rs deleted file mode 100644 index cc0852aab43..00000000000 --- a/library/std/src/sys/pal/unix/rand.rs +++ /dev/null @@ -1,302 +0,0 @@ -pub fn hashmap_random_keys() -> (u64, u64) { - const KEY_LEN: usize = core::mem::size_of::<u64>(); - - let mut v = [0u8; KEY_LEN * 2]; - if let Err(err) = read(&mut v) { - panic!("failed to retrieve random hash map seed: {err}"); - } - - let key1 = v[0..KEY_LEN].try_into().unwrap(); - let key2 = v[KEY_LEN..].try_into().unwrap(); - - (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) -} - -cfg_if::cfg_if! { - if #[cfg(any( - target_vendor = "apple", - target_os = "openbsd", - target_os = "emscripten", - target_os = "vita", - all(target_os = "netbsd", not(netbsd10)), - target_os = "fuchsia", - target_os = "vxworks", - ))] { - // Some systems have a syscall that directly retrieves random data. - // If that is guaranteed to be available, use it. - use imp::syscall as read; - } else { - // Otherwise, try the syscall to see if it exists only on some systems - // and fall back to reading from the random device otherwise. - fn read(bytes: &mut [u8]) -> crate::io::Result<()> { - use crate::fs::File; - use crate::io::Read; - use crate::sync::OnceLock; - - #[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "espidf", - target_os = "horizon", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "solaris", - target_os = "illumos", - netbsd10, - ))] - if let Some(res) = imp::syscall(bytes) { - return res; - } - - const PATH: &'static str = if cfg!(target_os = "redox") { - "/scheme/rand" - } else { - "/dev/urandom" - }; - - static FILE: OnceLock<File> = OnceLock::new(); - - FILE.get_or_try_init(|| File::open(PATH))?.read_exact(bytes) - } - } -} - -// All these systems a `getrandom` syscall. -// -// It is not guaranteed to be available, so return None to fallback to the file -// implementation. -#[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "espidf", - target_os = "horizon", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "solaris", - target_os = "illumos", - netbsd10, -))] -mod imp { - use crate::io::{Error, Result}; - use crate::sync::atomic::{AtomicBool, Ordering}; - use crate::sys::os::errno; - - #[cfg(any(target_os = "linux", target_os = "android"))] - fn getrandom(buf: &mut [u8]) -> libc::ssize_t { - use crate::sys::weak::syscall; - - // A weak symbol allows interposition, e.g. for perf measurements that want to - // disable randomness for consistency. Otherwise, we'll try a raw syscall. - // (`getrandom` was added in glibc 2.25, musl 1.1.20, android API level 28) - syscall! { - fn getrandom( - buffer: *mut libc::c_void, - length: libc::size_t, - flags: libc::c_uint - ) -> libc::ssize_t - } - - // This provides the best quality random numbers available at the given moment - // without ever blocking, and is preferable to falling back to /dev/urandom. - static GRND_INSECURE_AVAILABLE: AtomicBool = AtomicBool::new(true); - if GRND_INSECURE_AVAILABLE.load(Ordering::Relaxed) { - let ret = unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_INSECURE) }; - if ret == -1 && errno() as libc::c_int == libc::EINVAL { - GRND_INSECURE_AVAILABLE.store(false, Ordering::Relaxed); - } else { - return ret; - } - } - - unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) } - } - - #[cfg(any( - target_os = "dragonfly", - target_os = "espidf", - target_os = "horizon", - target_os = "freebsd", - netbsd10, - target_os = "illumos", - target_os = "solaris" - ))] - fn getrandom(buf: &mut [u8]) -> libc::ssize_t { - unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } - } - - pub fn syscall(v: &mut [u8]) -> Option<Result<()>> { - static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false); - - if GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed) { - return None; - } - - let mut read = 0; - while read < v.len() { - let result = getrandom(&mut v[read..]); - if result == -1 { - let err = errno() as libc::c_int; - if err == libc::EINTR { - continue; - } else if err == libc::ENOSYS || err == libc::EPERM { - // `getrandom` is not supported on the current system. - // - // Also fall back in case it is disabled by something like - // seccomp or inside of docker. - // - // If the `getrandom` syscall is not implemented in the current kernel version it should return an - // `ENOSYS` error. Docker also blocks the whole syscall inside unprivileged containers, and - // returns `EPERM` (instead of `ENOSYS`) when a program tries to invoke the syscall. Because of - // that we need to check for *both* `ENOSYS` and `EPERM`. - // - // Note that Docker's behavior is breaking other projects (notably glibc), so they're planning - // to update their filtering to return `ENOSYS` in a future release: - // - // https://github.com/moby/moby/issues/42680 - // - GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed); - return None; - } else if err == libc::EAGAIN { - // getrandom has failed because it would have blocked as the - // non-blocking pool (urandom) has not been initialized in - // the kernel yet due to a lack of entropy. Fallback to - // reading from `/dev/urandom` which will return potentially - // insecure random data to avoid blocking applications which - // could depend on this call without ever knowing they do and - // don't have a work around. - return None; - } else { - return Some(Err(Error::from_raw_os_error(err))); - } - } else { - read += result as usize; - } - } - - Some(Ok(())) - } -} - -#[cfg(any( - target_os = "macos", // Supported since macOS 10.12+. - target_os = "openbsd", - target_os = "emscripten", - target_os = "vita", -))] -mod imp { - use crate::io::{Error, Result}; - - pub fn syscall(v: &mut [u8]) -> Result<()> { - // getentropy(2) permits a maximum buffer size of 256 bytes - for s in v.chunks_mut(256) { - let ret = unsafe { libc::getentropy(s.as_mut_ptr().cast(), s.len()) }; - if ret == -1 { - return Err(Error::last_os_error()); - } - } - - Ok(()) - } -} - -// On Apple platforms, `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply -// call into `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes` -// manages a CSPRNG which is seeded from the kernel's CSPRNG and which runs on -// its own thread accessed via GCD. This seems needlessly heavyweight for our purposes -// so we only use it when `getentropy` is blocked, which appears to be the case -// on all platforms except macOS (see #102643). -// -// `CCRandomGenerateBytes` is used instead of `SecRandomCopyBytes` because the former is accessible -// via `libSystem` (libc) while the other needs to link to `Security.framework`. -#[cfg(all(target_vendor = "apple", not(target_os = "macos")))] -mod imp { - use libc::size_t; - - use crate::ffi::{c_int, c_void}; - use crate::io::{Error, Result}; - - pub fn syscall(v: &mut [u8]) -> Result<()> { - extern "C" { - fn CCRandomGenerateBytes(bytes: *mut c_void, count: size_t) -> c_int; - } - - let ret = unsafe { CCRandomGenerateBytes(v.as_mut_ptr().cast(), v.len()) }; - if ret != -1 { Ok(()) } else { Err(Error::last_os_error()) } - } -} - -// FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification. -#[cfg(all(target_os = "netbsd", not(netbsd10)))] -mod imp { - use crate::io::{Error, Result}; - use crate::ptr; - - pub fn syscall(v: &mut [u8]) -> Result<()> { - let mib = [libc::CTL_KERN, libc::KERN_ARND]; - // kern.arandom permits a maximum buffer size of 256 bytes - for s in v.chunks_mut(256) { - let mut s_len = s.len(); - let ret = unsafe { - libc::sysctl( - mib.as_ptr(), - mib.len() as libc::c_uint, - s.as_mut_ptr() as *mut _, - &mut s_len, - ptr::null(), - 0, - ) - }; - if ret == -1 { - return Err(Error::last_os_error()); - } else if s_len != s.len() { - // FIXME(joboet): this can't actually happen, can it? - panic!("read less bytes than requested from kern.arandom"); - } - } - - Ok(()) - } -} - -#[cfg(target_os = "fuchsia")] -mod imp { - use crate::io::Result; - - #[link(name = "zircon")] - extern "C" { - fn zx_cprng_draw(buffer: *mut u8, len: usize); - } - - pub fn syscall(v: &mut [u8]) -> Result<()> { - unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) }; - Ok(()) - } -} - -#[cfg(target_os = "vxworks")] -mod imp { - use core::sync::atomic::AtomicBool; - use core::sync::atomic::Ordering::Relaxed; - - use crate::io::{Error, Result}; - - pub fn syscall(v: &mut [u8]) -> Result<()> { - static RNG_INIT: AtomicBool = AtomicBool::new(false); - while !RNG_INIT.load(Relaxed) { - let ret = unsafe { libc::randSecure() }; - if ret < 0 { - return Err(Error::last_os_error()); - } else if ret > 0 { - RNG_INIT.store(true, Relaxed); - break; - } - - unsafe { libc::usleep(10) }; - } - - let ret = unsafe { - libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int) - }; - if ret >= 0 { Ok(()) } else { Err(Error::last_os_error()) } - } -} diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 9ff44b54c41..e0a0d0973c6 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -32,24 +32,24 @@ impl Drop for Handler { target_os = "macos", target_os = "netbsd", target_os = "openbsd", - target_os = "solaris" + target_os = "solaris", + target_os = "illumos", ))] mod imp { + use libc::{ + MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SA_ONSTACK, + SA_SIGINFO, SIG_DFL, SIGBUS, SIGSEGV, SS_DISABLE, sigaction, sigaltstack, sighandler_t, + }; #[cfg(not(all(target_os = "linux", target_env = "gnu")))] use libc::{mmap as mmap64, mprotect, munmap}; #[cfg(all(target_os = "linux", target_env = "gnu"))] use libc::{mmap64, mprotect, munmap}; - use libc::{ - sigaction, sigaltstack, sighandler_t, MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, - PROT_NONE, PROT_READ, PROT_WRITE, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSEGV, SIG_DFL, - SS_DISABLE, - }; use super::Handler; use crate::cell::Cell; use crate::ops::Range; - use crate::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sync::OnceLock; + use crate::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; use crate::{io, mem, ptr, thread}; @@ -280,7 +280,7 @@ mod imp { libc::SIGSTKSZ } - #[cfg(target_os = "solaris")] + #[cfg(any(target_os = "solaris", target_os = "illumos"))] unsafe fn get_stack_start() -> Option<*mut libc::c_void> { let mut current_stack: libc::stack_t = crate::mem::zeroed(); assert_eq!(libc::stack_getbounds(&mut current_stack), 0); @@ -486,7 +486,12 @@ mod imp { Some(guardaddr..guardaddr + page_size) } - #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] + #[cfg(any( + target_os = "macos", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + ))] // FIXME: I am probably not unsafe. unsafe fn current_guard() -> Option<Range<usize>> { let stackptr = get_stack_start()?; @@ -569,7 +574,8 @@ mod imp { target_os = "macos", target_os = "netbsd", target_os = "openbsd", - target_os = "solaris" + target_os = "solaris", + target_os = "illumos", )))] mod imp { pub unsafe fn init() {} diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index c9dcc5ad97a..7fe9b6c3e52 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -516,7 +516,7 @@ mod cgroups { use crate::borrow::Cow; use crate::ffi::OsString; - use crate::fs::{exists, File}; + use crate::fs::{File, exists}; use crate::io::{BufRead, BufReader, Read}; use crate::os::unix::ffi::OsStringExt; use crate::path::{Path, PathBuf}; diff --git a/library/std/src/sys/pal/unix/thread_parking.rs b/library/std/src/sys/pal/unix/thread_parking.rs index 1da5fce3cd3..72dd2031479 100644 --- a/library/std/src/sys/pal/unix/thread_parking.rs +++ b/library/std/src/sys/pal/unix/thread_parking.rs @@ -2,7 +2,7 @@ // separate modules for each platform. #![cfg(target_os = "netbsd")] -use libc::{_lwp_self, c_long, clockid_t, lwpid_t, time_t, timespec, CLOCK_MONOTONIC}; +use libc::{_lwp_self, CLOCK_MONOTONIC, c_long, clockid_t, lwpid_t, time_t, timespec}; use crate::ffi::{c_int, c_void}; use crate::ptr; diff --git a/library/std/src/sys/pal/unsupported/alloc.rs b/library/std/src/sys/pal/unsupported/alloc.rs deleted file mode 100644 index d715ae45401..00000000000 --- a/library/std/src/sys/pal/unsupported/alloc.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::ptr::null_mut; - -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { - null_mut() - } - - #[inline] - unsafe fn alloc_zeroed(&self, _layout: Layout) -> *mut u8 { - null_mut() - } - - #[inline] - unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} - - #[inline] - unsafe fn realloc(&self, _ptr: *mut u8, _layout: Layout, _new_size: usize) -> *mut u8 { - null_mut() - } -} diff --git a/library/std/src/sys/pal/unsupported/common.rs b/library/std/src/sys/pal/unsupported/common.rs index 76f80291f0e..34a76668383 100644 --- a/library/std/src/sys/pal/unsupported/common.rs +++ b/library/std/src/sys/pal/unsupported/common.rs @@ -27,7 +27,3 @@ pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { pub fn abort_internal() -> ! { core::intrinsics::abort(); } - -pub fn hashmap_random_keys() -> (u64, u64) { - (1, 2) -} diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index 442e6042ad5..01d516f7568 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -1,6 +1,5 @@ #![deny(unsafe_op_in_unsafe_fn)] -pub mod alloc; pub mod args; pub mod env; pub mod fs; diff --git a/library/std/src/sys/pal/unsupported/process.rs b/library/std/src/sys/pal/unsupported/process.rs index 40231bfc90b..fee81744f09 100644 --- a/library/std/src/sys/pal/unsupported/process.rs +++ b/library/std/src/sys/pal/unsupported/process.rs @@ -255,11 +255,11 @@ impl ExitStatusError { } #[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(bool); +pub struct ExitCode(u8); impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(false); - pub const FAILURE: ExitCode = ExitCode(true); + pub const SUCCESS: ExitCode = ExitCode(0); + pub const FAILURE: ExitCode = ExitCode(1); pub fn as_i32(&self) -> i32 { self.0 as i32 @@ -268,10 +268,7 @@ impl ExitCode { impl From<u8> for ExitCode { fn from(code: u8) -> Self { - match code { - 0 => Self::SUCCESS, - 1..=255 => Self::FAILURE, - } + Self(code) } } diff --git a/library/std/src/sys/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs index 88b1e543ec7..59ecc45b06a 100644 --- a/library/std/src/sys/pal/wasi/fs.rs +++ b/library/std/src/sys/pal/wasi/fs.rs @@ -13,7 +13,7 @@ use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::time::SystemTime; use crate::sys::unsupported; pub use crate::sys_common::fs::exists; -use crate::sys_common::{ignore_notfound, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner, ignore_notfound}; use crate::{fmt, iter, ptr}; pub struct File { @@ -265,7 +265,7 @@ impl OpenOptions { pub fn new() -> OpenOptions { let mut base = OpenOptions::default(); base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW; - return base; + base } pub fn read(&mut self, read: bool) { @@ -382,7 +382,7 @@ impl OpenOptions { base |= wasi::RIGHTS_PATH_UNLINK_FILE; base |= wasi::RIGHTS_POLL_FD_READWRITE; - return base; + base } fn rights_inheriting(&self) -> wasi::Rights { diff --git a/library/std/src/sys/pal/wasi/helpers.rs b/library/std/src/sys/pal/wasi/helpers.rs index d047bf2fce8..404747f0dc7 100644 --- a/library/std/src/sys/pal/wasi/helpers.rs +++ b/library/std/src/sys/pal/wasi/helpers.rs @@ -1,6 +1,6 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use crate::{io as std_io, mem}; +use crate::io as std_io; #[inline] pub fn is_interrupted(errno: i32) -> bool { @@ -108,16 +108,6 @@ pub fn abort_internal() -> ! { unsafe { libc::abort() } } -pub fn hashmap_random_keys() -> (u64, u64) { - let mut ret = (0u64, 0u64); - unsafe { - let base = &mut ret as *mut (u64, u64) as *mut u8; - let len = mem::size_of_val(&ret); - wasi::random_get(base, len).expect("random_get failure"); - } - return ret; -} - #[inline] pub(crate) fn err2io(err: wasi::Errno) -> std_io::Error { std_io::Error::from_raw_os_error(err.raw().into()) diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index f4dc3ebd414..5d54c790306 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -14,8 +14,6 @@ //! compiling for wasm. That way it's a compile time error for something that's //! guaranteed to be a runtime error! -#[path = "../unix/alloc.rs"] -pub mod alloc; pub mod args; pub mod env; pub mod fd; @@ -49,4 +47,4 @@ mod helpers; // then the compiler complains about conflicts. use helpers::err2io; -pub use helpers::{abort_internal, decode_error_kind, hashmap_random_keys, is_interrupted}; +pub use helpers::{abort_internal, decode_error_kind, is_interrupted}; diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 31c9cbd4699..4b83870fdea 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -136,36 +136,37 @@ impl Thread { } pub fn sleep(dur: Duration) { - let nanos = dur.as_nanos(); - assert!(nanos <= u64::MAX as u128); - - const USERDATA: wasi::Userdata = 0x0123_45678; - - let clock = wasi::SubscriptionClock { - id: wasi::CLOCKID_MONOTONIC, - timeout: nanos as u64, - precision: 0, - flags: 0, - }; - - let in_ = wasi::Subscription { - userdata: USERDATA, - u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, - }; - unsafe { - let mut event: wasi::Event = mem::zeroed(); - let res = wasi::poll_oneoff(&in_, &mut event, 1); - match (res, event) { - ( - Ok(1), - wasi::Event { - userdata: USERDATA, - error: wasi::ERRNO_SUCCESS, - type_: wasi::EVENTTYPE_CLOCK, - .. - }, - ) => {} - _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), + let mut nanos = dur.as_nanos(); + while nanos > 0 { + const USERDATA: wasi::Userdata = 0x0123_45678; + + let clock = wasi::SubscriptionClock { + id: wasi::CLOCKID_MONOTONIC, + timeout: u64::try_from(nanos).unwrap_or(u64::MAX), + precision: 0, + flags: 0, + }; + nanos -= u128::from(clock.timeout); + + let in_ = wasi::Subscription { + userdata: USERDATA, + u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, + }; + unsafe { + let mut event: wasi::Event = mem::zeroed(); + let res = wasi::poll_oneoff(&in_, &mut event, 1); + match (res, event) { + ( + Ok(1), + wasi::Event { + userdata: USERDATA, + error: wasi::ERRNO_SUCCESS, + type_: wasi::EVENTTYPE_CLOCK, + .. + }, + ) => {} + _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), + } } } } diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index f20630e10cf..17b26543bd7 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -6,8 +6,6 @@ //! To begin with, this target mirrors the wasi target 1 to 1, but over //! time this will change significantly. -#[path = "../unix/alloc.rs"] -pub mod alloc; #[path = "../wasi/args.rs"] pub mod args; #[path = "../wasi/env.rs"] @@ -52,6 +50,6 @@ mod helpers; // then the compiler complains about conflicts. use helpers::err2io; -pub use helpers::{abort_internal, decode_error_kind, hashmap_random_keys, is_interrupted}; +pub use helpers::{abort_internal, decode_error_kind, is_interrupted}; mod cabi_realloc; diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 4c34859e918..8141bfac49a 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -16,7 +16,6 @@ #![deny(unsafe_op_in_unsafe_fn)] -pub mod alloc; #[path = "../unsupported/args.rs"] pub mod args; pub mod env; diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index 00c816a6c09..9e336ff2d47 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -254,7 +254,7 @@ pub struct WinError { pub code: u32, } impl WinError { - const fn new(code: u32) -> Self { + pub const fn new(code: u32) -> Self { Self { code } } } @@ -272,8 +272,11 @@ impl WinError { // tidy-alphabetical-start pub const ACCESS_DENIED: Self = Self::new(c::ERROR_ACCESS_DENIED); pub const ALREADY_EXISTS: Self = Self::new(c::ERROR_ALREADY_EXISTS); + pub const BAD_NET_NAME: Self = Self::new(c::ERROR_BAD_NET_NAME); + pub const BAD_NETPATH: Self = Self::new(c::ERROR_BAD_NETPATH); pub const CANT_ACCESS_FILE: Self = Self::new(c::ERROR_CANT_ACCESS_FILE); pub const DELETE_PENDING: Self = Self::new(c::ERROR_DELETE_PENDING); + pub const DIR_NOT_EMPTY: Self = Self::new(c::ERROR_DIR_NOT_EMPTY); pub const DIRECTORY: Self = Self::new(c::ERROR_DIRECTORY); pub const FILE_NOT_FOUND: Self = Self::new(c::ERROR_FILE_NOT_FOUND); pub const INSUFFICIENT_BUFFER: Self = Self::new(c::ERROR_INSUFFICIENT_BUFFER); diff --git a/library/std/src/sys/pal/windows/args.rs b/library/std/src/sys/pal/windows/args.rs index 66e75a83571..848632ec2a7 100644 --- a/library/std/src/sys/pal/windows/args.rs +++ b/library/std/src/sys/pal/windows/args.rs @@ -14,13 +14,13 @@ use crate::path::{Path, PathBuf}; use crate::sys::path::get_long_path; use crate::sys::process::ensure_no_nuls; use crate::sys::{c, to_u16s}; -use crate::sys_common::wstr::WStrUnits; use crate::sys_common::AsInner; +use crate::sys_common::wstr::WStrUnits; use crate::{fmt, io, iter, vec}; /// This is the const equivalent to `NonZero::new(n).unwrap()` /// -/// FIXME: This can be removed once `Option::unwrap` is stably const. +/// FIXME(const-hack): This can be removed once `Option::unwrap` is stably const. /// See the `const_option` feature (#67441). const fn non_zero_u16(n: u16) -> NonZero<u16> { match NonZero::new(n) { diff --git a/library/std/src/sys/pal/windows/args/tests.rs b/library/std/src/sys/pal/windows/args/tests.rs index 484a90ab056..6d5c953cbd5 100644 --- a/library/std/src/sys/pal/windows/args/tests.rs +++ b/library/std/src/sys/pal/windows/args/tests.rs @@ -47,10 +47,10 @@ fn whitespace_behavior() { fn genius_quotes() { chk(r#"EXE "" """#, &["EXE", "", ""]); chk(r#"EXE "" """"#, &["EXE", "", r#"""#]); - chk( - r#"EXE "this is """all""" in the same argument""#, - &["EXE", r#"this is "all" in the same argument"#], - ); + chk(r#"EXE "this is """all""" in the same argument""#, &[ + "EXE", + r#"this is "all" in the same argument"#, + ]); chk(r#"EXE "a"""#, &["EXE", r#"a""#]); chk(r#"EXE "a"" a"#, &["EXE", r#"a" a"#]); // quotes cannot be escaped in command names diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 2f5d75dc4bc..b65ad7dbe8c 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -5,7 +5,7 @@ #![unstable(issue = "none", feature = "windows_c")] #![allow(clippy::style)] -use core::ffi::{c_uint, c_ulong, c_ushort, c_void, CStr}; +use core::ffi::{CStr, c_uint, c_ulong, c_ushort, c_void}; use core::{mem, ptr}; mod windows_sys; @@ -109,19 +109,15 @@ if #[cfg(not(target_vendor = "uwp"))] { } // Use raw-dylib to import ProcessPrng as we can't rely on there being an import library. -cfg_if::cfg_if! { -if #[cfg(not(target_vendor = "win7"))] { - #[cfg(target_arch = "x86")] - #[link(name = "bcryptprimitives", kind = "raw-dylib", import_name_type = "undecorated")] - extern "system" { - pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; - } - #[cfg(not(target_arch = "x86"))] - #[link(name = "bcryptprimitives", kind = "raw-dylib")] - extern "system" { - pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; - } -}} +#[cfg(not(target_vendor = "win7"))] +#[cfg_attr( + target_arch = "x86", + link(name = "bcryptprimitives", kind = "raw-dylib", import_name_type = "undecorated") +)] +#[cfg_attr(not(target_arch = "x86"), link(name = "bcryptprimitives", kind = "raw-dylib"))] +extern "system" { + pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; +} // Functions that aren't available on every version of Windows that we support, // but we still use them and just provide some form of a fallback implementation. diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index afacc370c34..9c2e4500da0 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -34,6 +34,7 @@ Windows.Wdk.Storage.FileSystem.FILE_WRITE_THROUGH Windows.Wdk.Storage.FileSystem.NtCreateFile Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_DISPOSITION Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_OPTIONS +Windows.Wdk.Storage.FileSystem.NtOpenFile Windows.Wdk.Storage.FileSystem.NtReadFile Windows.Wdk.Storage.FileSystem.NtWriteFile Windows.Wdk.Storage.FileSystem.SYMLINK_FLAG_RELATIVE @@ -1931,10 +1932,14 @@ Windows.Win32.Foundation.RtlNtStatusToDosError Windows.Win32.Foundation.SetHandleInformation Windows.Win32.Foundation.SetLastError Windows.Win32.Foundation.STATUS_DELETE_PENDING +Windows.Win32.Foundation.STATUS_DIRECTORY_NOT_EMPTY Windows.Win32.Foundation.STATUS_END_OF_FILE +Windows.Win32.Foundation.STATUS_FILE_DELETED +Windows.Win32.Foundation.STATUS_INVALID_HANDLE Windows.Win32.Foundation.STATUS_INVALID_PARAMETER Windows.Win32.Foundation.STATUS_NOT_IMPLEMENTED Windows.Win32.Foundation.STATUS_PENDING +Windows.Win32.Foundation.STATUS_SHARING_VIOLATION Windows.Win32.Foundation.STATUS_SUCCESS Windows.Win32.Foundation.TRUE Windows.Win32.Foundation.UNICODE_STRING diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 529c96a0e1e..ab5f8919d7a 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -105,6 +105,7 @@ windows_targets::link!("kernel32.dll" "system" fn WideCharToMultiByte(codepage : windows_targets::link!("kernel32.dll" "system" fn WriteConsoleW(hconsoleoutput : HANDLE, lpbuffer : PCWSTR, nnumberofcharstowrite : u32, lpnumberofcharswritten : *mut u32, lpreserved : *const core::ffi::c_void) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn WriteFileEx(hfile : HANDLE, lpbuffer : *const u8, nnumberofbytestowrite : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPOVERLAPPED_COMPLETION_ROUTINE) -> BOOL); windows_targets::link!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : FILE_ACCESS_RIGHTS, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : FILE_FLAGS_AND_ATTRIBUTES, shareaccess : FILE_SHARE_MODE, createdisposition : NTCREATEFILE_CREATE_DISPOSITION, createoptions : NTCREATEFILE_CREATE_OPTIONS, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS); +windows_targets::link!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : u32, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS); windows_targets::link!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); windows_targets::link!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); windows_targets::link!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32); @@ -2982,10 +2983,14 @@ pub struct STARTUPINFOW { } pub type STARTUPINFOW_FLAGS = u32; pub const STATUS_DELETE_PENDING: NTSTATUS = 0xC0000056_u32 as _; +pub const STATUS_DIRECTORY_NOT_EMPTY: NTSTATUS = 0xC0000101_u32 as _; pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _; +pub const STATUS_FILE_DELETED: NTSTATUS = 0xC0000123_u32 as _; +pub const STATUS_INVALID_HANDLE: NTSTATUS = 0xC0000008_u32 as _; pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xC000000D_u32 as _; pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002_u32 as _; pub const STATUS_PENDING: NTSTATUS = 0x103_u32 as _; +pub const STATUS_SHARING_VIOLATION: NTSTATUS = 0xC0000043_u32 as _; pub const STATUS_SUCCESS: NTSTATUS = 0x0_u32 as _; pub const STD_ERROR_HANDLE: STD_HANDLE = 4294967284u32; pub type STD_HANDLE = u32; diff --git a/library/std/src/sys/pal/windows/compat.rs b/library/std/src/sys/pal/windows/compat.rs index 75232dfc0b0..c8e25dd0c94 100644 --- a/library/std/src/sys/pal/windows/compat.rs +++ b/library/std/src/sys/pal/windows/compat.rs @@ -19,7 +19,7 @@ //! function is called. In the worst case, multiple threads may all end up //! importing the same function unnecessarily. -use crate::ffi::{c_void, CStr}; +use crate::ffi::{CStr, c_void}; use crate::ptr::NonNull; use crate::sys::c; diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index 2134152ea93..be26356bb40 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -1,9 +1,9 @@ use core::ptr::addr_of; use super::api::{self, WinError}; -use super::{to_u16s, IoResult}; +use super::{IoResult, to_u16s}; use crate::borrow::Cow; -use crate::ffi::{c_void, OsStr, OsString}; +use crate::ffi::{OsStr, OsString, c_void}; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem::{self, MaybeUninit}; use crate::os::windows::io::{AsHandle, BorrowedHandle}; @@ -13,9 +13,12 @@ use crate::sync::Arc; use crate::sys::handle::Handle; use crate::sys::path::maybe_verbatim; use crate::sys::time::SystemTime; -use crate::sys::{c, cvt, Align8}; -use crate::sys_common::{ignore_notfound, AsInner, FromInner, IntoInner}; -use crate::{fmt, ptr, slice, thread}; +use crate::sys::{Align8, c, cvt}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::{fmt, ptr, slice}; + +mod remove_dir_all; +use remove_dir_all::remove_dir_all_iterative; pub struct File { handle: Handle, @@ -646,6 +649,22 @@ impl File { Ok(info) } } + + /// Deletes the file, consuming the file handle to ensure the delete occurs + /// as immediately as possible. + /// This attempts to use `posix_delete` but falls back to `win32_delete` + /// if that is not supported by the filesystem. + #[allow(unused)] + fn delete(self) -> Result<(), WinError> { + // If POSIX delete is not supported for this filesystem then fallback to win32 delete. + match self.posix_delete() { + Err(WinError::INVALID_PARAMETER) + | Err(WinError::NOT_SUPPORTED) + | Err(WinError::INVALID_FUNCTION) => self.win32_delete(), + result => result, + } + } + /// Delete using POSIX semantics. /// /// Files will be deleted as soon as the handle is closed. This is supported @@ -654,21 +673,23 @@ impl File { /// /// If the operation is not supported for this filesystem or OS version /// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`. - fn posix_delete(&self) -> io::Result<()> { + #[allow(unused)] + fn posix_delete(&self) -> Result<(), WinError> { let info = c::FILE_DISPOSITION_INFO_EX { Flags: c::FILE_DISPOSITION_FLAG_DELETE | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE, }; - api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info) } /// Delete a file using win32 semantics. The file won't actually be deleted /// until all file handles are closed. However, marking a file for deletion /// will prevent anyone from opening a new handle to the file. - fn win32_delete(&self) -> io::Result<()> { + #[allow(unused)] + fn win32_delete(&self) -> Result<(), WinError> { let info = c::FILE_DISPOSITION_INFO { DeleteFile: c::TRUE as _ }; - api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info) } /// Fill the given buffer with as many directory entries as will fit. @@ -684,21 +705,23 @@ impl File { /// A symlink directory is simply an empty directory with some "reparse" metadata attached. /// So if you open a link (not its target) and iterate the directory, /// you will always iterate an empty directory regardless of the target. - fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> io::Result<bool> { + #[allow(unused)] + fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> Result<bool, WinError> { let class = if restart { c::FileIdBothDirectoryRestartInfo } else { c::FileIdBothDirectoryInfo }; unsafe { - let result = cvt(c::GetFileInformationByHandleEx( - self.handle.as_raw_handle(), + let result = c::GetFileInformationByHandleEx( + self.as_raw_handle(), class, buffer.as_mut_ptr().cast(), buffer.capacity() as _, - )); - match result { - Ok(_) => Ok(true), - Err(e) if e.raw_os_error() == Some(c::ERROR_NO_MORE_FILES as _) => Ok(false), - Err(e) => Err(e), + ); + if result == 0 { + let err = api::get_last_error(); + if err.code == c::ERROR_NO_MORE_FILES { Ok(false) } else { Err(err) } + } else { + Ok(true) } } } @@ -804,62 +827,6 @@ unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> } } -/// Open a link relative to the parent directory, ensure no symlinks are followed. -fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<File> { - // This is implemented using the lower level `NtCreateFile` function as - // unfortunately opening a file relative to a parent is not supported by - // win32 functions. It is however a fundamental feature of the NT kernel. - // - // See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile - unsafe { - let mut handle = ptr::null_mut(); - let mut io_status = c::IO_STATUS_BLOCK::PENDING; - let mut name_str = c::UNICODE_STRING::from_ref(name); - use crate::sync::atomic::{AtomicU32, Ordering}; - // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been - // tricked into following a symlink. However, it may not be available in - // earlier versions of Windows. - static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE); - let object = c::OBJECT_ATTRIBUTES { - ObjectName: &mut name_str, - RootDirectory: parent.as_raw_handle(), - Attributes: ATTRIBUTES.load(Ordering::Relaxed), - ..c::OBJECT_ATTRIBUTES::default() - }; - let status = c::NtCreateFile( - &mut handle, - access, - &object, - &mut io_status, - crate::ptr::null_mut(), - 0, - c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE, - c::FILE_OPEN, - // If `name` is a symlink then open the link rather than the target. - c::FILE_OPEN_REPARSE_POINT, - crate::ptr::null_mut(), - 0, - ); - // Convert an NTSTATUS to the more familiar Win32 error codes (aka "DosError") - if c::nt_success(status) { - Ok(File::from_raw_handle(handle)) - } else if status == c::STATUS_DELETE_PENDING { - // We make a special exception for `STATUS_DELETE_PENDING` because - // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is - // very unhelpful. - Err(io::Error::from_raw_os_error(c::ERROR_DELETE_PENDING as i32)) - } else if status == c::STATUS_INVALID_PARAMETER - && ATTRIBUTES.load(Ordering::Relaxed) == c::OBJ_DONT_REPARSE - { - // Try without `OBJ_DONT_REPARSE`. See above. - ATTRIBUTES.store(0, Ordering::Relaxed); - open_link_no_reparse(parent, name, access) - } else { - Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as _)) - } - } -} - impl AsInner<Handle> for File { #[inline] fn as_inner(&self) -> &Handle { @@ -1142,114 +1109,22 @@ pub fn rmdir(p: &Path) -> io::Result<()> { Ok(()) } -/// Open a file or directory without following symlinks. -fn open_link(path: &Path, access_mode: u32) -> io::Result<File> { +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + // Open a file or directory without following symlinks. let mut opts = OpenOptions::new(); - opts.access_mode(access_mode); + opts.access_mode(c::FILE_LIST_DIRECTORY); // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories. // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target. opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); - File::open(path, &opts) -} - -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - let file = open_link(path, c::DELETE | c::FILE_LIST_DIRECTORY)?; + let file = File::open(path, &opts)?; // Test if the file is not a directory or a symlink to a directory. if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 { return Err(io::Error::from_raw_os_error(c::ERROR_DIRECTORY as _)); } - match ignore_notfound(remove_dir_all_iterative(&file, File::posix_delete)) { - Err(e) => { - if let Some(code) = e.raw_os_error() { - match code as u32 { - // If POSIX delete is not supported for this filesystem then fallback to win32 delete. - c::ERROR_NOT_SUPPORTED - | c::ERROR_INVALID_FUNCTION - | c::ERROR_INVALID_PARAMETER => { - remove_dir_all_iterative(&file, File::win32_delete) - } - _ => Err(e), - } - } else { - Err(e) - } - } - ok => ok, - } -} - -fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io::Result<()> { - // When deleting files we may loop this many times when certain error conditions occur. - // This allows remove_dir_all to succeed when the error is temporary. - const MAX_RETRIES: u32 = 10; - - let mut buffer = DirBuff::new(); - let mut dirlist = vec![f.duplicate()?]; - - // FIXME: This is a hack so we can push to the dirlist vec after borrowing from it. - fn copy_handle(f: &File) -> mem::ManuallyDrop<File> { - unsafe { mem::ManuallyDrop::new(File::from_raw_handle(f.as_raw_handle())) } - } - - let mut restart = true; - while let Some(dir) = dirlist.last() { - let dir = copy_handle(dir); - - // Fill the buffer and iterate the entries. - let more_data = dir.fill_dir_buff(&mut buffer, restart)?; - restart = false; - for (name, is_directory) in buffer.iter() { - if is_directory { - let child_dir = open_link_no_reparse( - &dir, - &name, - c::SYNCHRONIZE | c::DELETE | c::FILE_LIST_DIRECTORY, - ); - // On success, add the handle to the queue. - // If opening the directory fails we treat it the same as a file - if let Ok(child_dir) = child_dir { - dirlist.push(child_dir); - continue; - } - } - for i in 1..=MAX_RETRIES { - let result = open_link_no_reparse(&dir, &name, c::SYNCHRONIZE | c::DELETE); - match result { - Ok(f) => delete(&f)?, - // Already deleted, so skip. - Err(e) if e.kind() == io::ErrorKind::NotFound => break, - // Retry a few times if the file is locked or a delete is already in progress. - Err(e) - if i < MAX_RETRIES - && (e.raw_os_error() == Some(c::ERROR_DELETE_PENDING as _) - || e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as _)) => {} - // Otherwise return the error. - Err(e) => return Err(e), - } - thread::yield_now(); - } - } - // If there were no more files then delete the directory. - if !more_data { - if let Some(dir) = dirlist.pop() { - // Retry deleting a few times in case we need to wait for a file to be deleted. - for i in 1..=MAX_RETRIES { - let result = delete(&dir); - if let Err(e) = result { - if i == MAX_RETRIES || e.kind() != io::ErrorKind::DirectoryNotEmpty { - return Err(e); - } - thread::yield_now(); - } else { - break; - } - } - } - } - } - Ok(()) + // Remove the directory and all its contents. + remove_dir_all_iterative(file).io_result() } pub fn readlink(path: &Path) -> io::Result<PathBuf> { diff --git a/library/std/src/sys/pal/windows/fs/remove_dir_all.rs b/library/std/src/sys/pal/windows/fs/remove_dir_all.rs new file mode 100644 index 00000000000..9416049da78 --- /dev/null +++ b/library/std/src/sys/pal/windows/fs/remove_dir_all.rs @@ -0,0 +1,205 @@ +//! The Windows implementation of std::fs::remove_dir_all. +//! +//! This needs to address two issues: +//! +//! - It must not be possible to trick this into deleting files outside of +//! the parent directory (see CVE-2022-21658). +//! - It should not fail if many threads or processes call `remove_dir_all` +//! on the same path. +//! +//! The first is handled by using the low-level `NtOpenFile` API to open a file +//! relative to a parent directory. +//! +//! The second is trickier. Deleting a file works by setting its "disposition" +//! to delete. However, it isn't actually deleted until the file is closed. +//! During the gap between these two events, the file is in a kind of limbo +//! state where it still exists in the filesystem but anything trying to open +//! it fails with an error. +//! +//! The mitigations we use here are: +//! +//! - When attempting to open the file, we treat ERROR_DELETE_PENDING as a +//! successful delete. +//! - If the file still hasn't been removed from the filesystem by the time we +//! attempt to delete the parent directory, we try to wait for it to finish. +//! We can't wait indefinitely though so after some number of spins, we give +//! up and return an error. +//! +//! In short, we can't guarantee this will always succeed in the event of a +//! race but we do make a best effort such that it *should* do so. + +use core::ptr; +use core::sync::atomic::{AtomicU32, Ordering}; + +use super::{AsRawHandle, DirBuff, File, FromRawHandle}; +use crate::sys::c; +use crate::sys::pal::windows::api::WinError; +use crate::thread; + +// The maximum number of times to spin when waiting for deletes to complete. +const MAX_RETRIES: usize = 50; + +/// A wrapper around a raw NtOpenFile call. +/// +/// This isn't completely safe because `OBJECT_ATTRIBUTES` contains raw pointers. +unsafe fn nt_open_file( + access: u32, + object_attribute: &c::OBJECT_ATTRIBUTES, + share: u32, + options: u32, +) -> Result<File, WinError> { + unsafe { + let mut handle = ptr::null_mut(); + let mut io_status = c::IO_STATUS_BLOCK::PENDING; + let status = + c::NtOpenFile(&mut handle, access, object_attribute, &mut io_status, share, options); + if c::nt_success(status) { + Ok(File::from_raw_handle(handle)) + } else { + // Convert an NTSTATUS to the more familiar Win32 error code (aka "DosError") + let win_error = if status == c::STATUS_DELETE_PENDING { + // We make a special exception for `STATUS_DELETE_PENDING` because + // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is + // very unhelpful because that can also mean a permission error. + WinError::DELETE_PENDING + } else { + WinError::new(c::RtlNtStatusToDosError(status)) + }; + Err(win_error) + } + } +} + +/// Open the file `path` in the directory `parent`, requesting the given `access` rights. +/// `options` will be OR'd with `FILE_OPEN_REPARSE_POINT`. +fn open_link_no_reparse( + parent: &File, + path: &[u16], + access: u32, + options: u32, +) -> Result<Option<File>, WinError> { + // This is implemented using the lower level `NtOpenFile` function as + // unfortunately opening a file relative to a parent is not supported by + // win32 functions. + // + // See https://learn.microsoft.com/windows/win32/api/winternl/nf-winternl-ntopenfile + + // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been + // tricked into following a symlink. However, it may not be available in + // earlier versions of Windows. + static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE); + + let result = unsafe { + let mut path_str = c::UNICODE_STRING::from_ref(path); + let mut object = c::OBJECT_ATTRIBUTES { + ObjectName: &mut path_str, + RootDirectory: parent.as_raw_handle(), + Attributes: ATTRIBUTES.load(Ordering::Relaxed), + ..c::OBJECT_ATTRIBUTES::default() + }; + let share = c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE; + let options = c::FILE_OPEN_REPARSE_POINT | options; + let result = nt_open_file(access, &object, share, options); + + // Retry without OBJ_DONT_REPARSE if it's not supported. + if matches!(result, Err(WinError::INVALID_PARAMETER)) + && ATTRIBUTES.load(Ordering::Relaxed) == c::OBJ_DONT_REPARSE + { + ATTRIBUTES.store(0, Ordering::Relaxed); + object.Attributes = 0; + nt_open_file(access, &object, share, options) + } else { + result + } + }; + + // Ignore not found errors + match result { + Ok(f) => Ok(Some(f)), + Err( + WinError::FILE_NOT_FOUND + | WinError::PATH_NOT_FOUND + | WinError::BAD_NETPATH + | WinError::BAD_NET_NAME + // `DELETE_PENDING` means something else is already trying to delete it + // so we assume that will eventually succeed. + | WinError::DELETE_PENDING, + ) => Ok(None), + Err(e) => Err(e), + } +} + +fn open_dir(parent: &File, name: &[u16]) -> Result<Option<File>, WinError> { + // Open the directory for synchronous directory listing. + open_link_no_reparse( + parent, + name, + c::SYNCHRONIZE | c::FILE_LIST_DIRECTORY, + // "_IO_NONALERT" means that a synchronous call won't be interrupted. + c::FILE_SYNCHRONOUS_IO_NONALERT, + ) +} + +fn delete(parent: &File, name: &[u16]) -> Result<(), WinError> { + // Note that the `delete` function consumes the opened file to ensure it's + // dropped immediately. See module comments for why this is important. + match open_link_no_reparse(parent, name, c::DELETE, 0) { + Ok(Some(f)) => f.delete(), + Ok(None) => Ok(()), + Err(e) => Err(e), + } +} + +/// A simple retry loop that keeps running `f` while it fails with the given +/// error code or until `MAX_RETRIES` is reached. +fn retry<T: PartialEq>( + mut f: impl FnMut() -> Result<T, WinError>, + ignore: WinError, +) -> Result<T, WinError> { + let mut i = MAX_RETRIES; + loop { + i -= 1; + if i == 0 { + return f(); + } else { + let result = f(); + if result != Err(ignore) { + return result; + } + } + thread::yield_now(); + } +} + +pub fn remove_dir_all_iterative(dir: File) -> Result<(), WinError> { + let mut buffer = DirBuff::new(); + let mut dirlist = vec![dir]; + + let mut restart = true; + 'outer: while let Some(dir) = dirlist.pop() { + let more_data = dir.fill_dir_buff(&mut buffer, restart)?; + for (name, is_directory) in buffer.iter() { + if is_directory { + let Some(subdir) = open_dir(&dir, &name)? else { continue }; + dirlist.push(dir); + dirlist.push(subdir); + continue 'outer; + } else { + // Attempt to delete, retrying on sharing violation errors as these + // can often be very temporary. E.g. if something takes just a + // bit longer than expected to release a file handle. + retry(|| delete(&dir, &name), WinError::SHARING_VIOLATION)?; + } + } + if more_data { + dirlist.push(dir); + restart = false; + } else { + // Attempt to delete, retrying on not empty errors because we may + // need to wait some time for files to be removed from the filesystem. + retry(|| delete(&dir, &[]), WinError::DIR_NOT_EMPTY)?; + restart = true; + } + } + Ok(()) +} diff --git a/library/std/src/sys/pal/windows/futex.rs b/library/std/src/sys/pal/windows/futex.rs index 8c5081a607a..f16a9f534a3 100644 --- a/library/std/src/sys/pal/windows/futex.rs +++ b/library/std/src/sys/pal/windows/futex.rs @@ -1,7 +1,7 @@ use core::ffi::c_void; use core::sync::atomic::{ - AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, - AtomicU32, AtomicU64, AtomicU8, AtomicUsize, + AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize, AtomicPtr, AtomicU8, + AtomicU16, AtomicU32, AtomicU64, AtomicUsize, }; use core::time::Duration; use core::{mem, ptr}; diff --git a/library/std/src/sys/pal/windows/handle/tests.rs b/library/std/src/sys/pal/windows/handle/tests.rs index d836dae4c30..0c976ed84e6 100644 --- a/library/std/src/sys/pal/windows/handle/tests.rs +++ b/library/std/src/sys/pal/windows/handle/tests.rs @@ -1,4 +1,4 @@ -use crate::sys::pipe::{anon_pipe, Pipes}; +use crate::sys::pipe::{Pipes, anon_pipe}; use crate::{thread, time}; /// Test the synchronous fallback for overlapped I/O. diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 6ed77fbc3d4..1ea253e5e52 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -1,7 +1,6 @@ #![allow(missing_docs, nonstandard_style)] #![forbid(unsafe_op_in_unsafe_fn)] -pub use self::rand::hashmap_random_keys; use crate::ffi::{OsStr, OsString}; use crate::io::ErrorKind; use crate::mem::MaybeUninit; @@ -13,9 +12,8 @@ use crate::time::Duration; #[macro_use] pub mod compat; -mod api; +pub mod api; -pub mod alloc; pub mod args; pub mod c; pub mod env; @@ -28,7 +26,6 @@ pub mod net; pub mod os; pub mod pipe; pub mod process; -pub mod rand; pub mod stdio; pub mod thread; pub mod time; @@ -123,6 +120,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { c::ERROR_NOT_SAME_DEVICE => return CrossesDevices, c::ERROR_TOO_MANY_LINKS => return TooManyLinks, c::ERROR_FILENAME_EXCED_RANGE => return InvalidFilename, + c::ERROR_CANT_RESOLVE_FILENAME => return FilesystemLoop, _ => {} } @@ -140,6 +138,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { c::WSAEHOSTUNREACH => HostUnreachable, c::WSAENETDOWN => NetworkDown, c::WSAENETUNREACH => NetworkUnreachable, + c::WSAEDQUOT => FilesystemQuotaExceeded, _ => Uncategorized, } diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs index ce995f5ed5a..61a4504cf65 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/pal/windows/net.rs @@ -9,7 +9,7 @@ use crate::os::windows::io::{ }; use crate::sync::OnceLock; use crate::sys::c; -use crate::sys_common::{net, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner, net}; use crate::time::Duration; use crate::{cmp, mem, ptr, sys}; @@ -27,12 +27,12 @@ pub mod netc { use crate::sys::c::{self, ADDRESS_FAMILY, ADDRINFOA, SOCKADDR, SOCKET}; // re-exports from Windows API bindings. pub use crate::sys::c::{ - bind, connect, freeaddrinfo, getpeername, getsockname, getsockopt, listen, setsockopt, - ADDRESS_FAMILY as sa_family_t, ADDRINFOA as addrinfo, IPPROTO_IP, IPPROTO_IPV6, - IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, - IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, - SOCKADDR as sockaddr, SOCKADDR_STORAGE as sockaddr_storage, SOCK_DGRAM, SOCK_STREAM, - SOL_SOCKET, SO_BROADCAST, SO_RCVTIMEO, SO_SNDTIMEO, + ADDRESS_FAMILY as sa_family_t, ADDRINFOA as addrinfo, IP_ADD_MEMBERSHIP, + IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, IPPROTO_IP, IPPROTO_IPV6, + IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, SO_BROADCAST, + SO_RCVTIMEO, SO_SNDTIMEO, SOCK_DGRAM, SOCK_STREAM, SOCKADDR as sockaddr, + SOCKADDR_STORAGE as sockaddr_storage, SOL_SOCKET, bind, connect, freeaddrinfo, getpeername, + getsockname, getsockopt, listen, setsockopt, }; #[allow(non_camel_case_types)] diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 7d1b5aca1d5..d8200ef9ca4 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -2,12 +2,13 @@ use crate::ffi::OsStr; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::os::windows::prelude::*; use crate::path::Path; +use crate::random::{DefaultRandomSource, Random}; use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::Relaxed; +use crate::sys::c; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; use crate::sys::pal::windows::api::{self, WinError}; -use crate::sys::{c, hashmap_random_keys}; use crate::sys_common::{FromInner, IntoInner}; use crate::{mem, ptr}; @@ -79,7 +80,7 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res name = format!( r"\\.\pipe\__rust_anonymous_pipe1__.{}.{}", c::GetCurrentProcessId(), - random_number() + random_number(), ); let wide_name = OsStr::new(&name).encode_wide().chain(Some(0)).collect::<Vec<_>>(); let mut flags = c::FILE_FLAG_FIRST_PIPE_INSTANCE | c::FILE_FLAG_OVERLAPPED; @@ -214,7 +215,7 @@ fn random_number() -> usize { return N.fetch_add(1, Relaxed); } - N.store(hashmap_random_keys().0 as usize, Relaxed); + N.store(usize::random(&mut DefaultRandomSource), Relaxed); } } diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs index 06eae5a07b0..93a6c45ce30 100644 --- a/library/std/src/sys/pal/windows/process.rs +++ b/library/std/src/sys/pal/windows/process.rs @@ -22,8 +22,8 @@ use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; use crate::sys::pipe::{self, AnonPipe}; use crate::sys::{cvt, path, stdio}; -use crate::sys_common::process::{CommandEnv, CommandEnvs}; use crate::sys_common::IntoInner; +use crate::sys_common::process::{CommandEnv, CommandEnvs}; use crate::{cmp, env, fmt, mem, ptr}; //////////////////////////////////////////////////////////////////////////////// @@ -253,10 +253,10 @@ impl Command { attribute: usize, value: T, ) { - self.proc_thread_attributes.insert( - attribute, - ProcThreadAttributeValue { size: mem::size_of::<T>(), data: Box::new(value) }, - ); + self.proc_thread_attributes.insert(attribute, ProcThreadAttributeValue { + size: mem::size_of::<T>(), + data: Box::new(value), + }); } pub fn spawn( @@ -272,11 +272,24 @@ impl Command { None }; let program = resolve_exe(&self.program, || env::var_os("PATH"), child_paths)?; - // Case insensitive "ends_with" of UTF-16 encoded ".bat" or ".cmd" - let is_batch_file = matches!( - program.len().checked_sub(5).and_then(|i| program.get(i..)), - Some([46, 98 | 66, 97 | 65, 116 | 84, 0] | [46, 99 | 67, 109 | 77, 100 | 68, 0]) - ); + let has_bat_extension = |program: &[u16]| { + matches!( + // Case insensitive "ends_with" of UTF-16 encoded ".bat" or ".cmd" + program.len().checked_sub(4).and_then(|i| program.get(i..)), + Some([46, 98 | 66, 97 | 65, 116 | 84] | [46, 99 | 67, 109 | 77, 100 | 68]) + ) + }; + let is_batch_file = if path::is_verbatim(&program) { + has_bat_extension(&program[..program.len() - 1]) + } else { + super::fill_utf16_buf( + |buffer, size| unsafe { + // resolve the path so we can test the final file name. + c::GetFullPathNameW(program.as_ptr(), size, buffer, ptr::null_mut()) + }, + |program| has_bat_extension(program), + )? + }; let (program, mut cmd_str) = if is_batch_file { ( command_prompt()?, diff --git a/library/std/src/sys/pal/windows/process/tests.rs b/library/std/src/sys/pal/windows/process/tests.rs index 65325fa64a0..b567151b721 100644 --- a/library/std/src/sys/pal/windows/process/tests.rs +++ b/library/std/src/sys/pal/windows/process/tests.rs @@ -1,4 +1,4 @@ -use super::{make_command_line, Arg}; +use super::{Arg, make_command_line}; use crate::env; use crate::ffi::{OsStr, OsString}; use crate::process::Command; diff --git a/library/std/src/sys/pal/windows/rand.rs b/library/std/src/sys/pal/windows/rand.rs deleted file mode 100644 index e366bb99562..00000000000 --- a/library/std/src/sys/pal/windows/rand.rs +++ /dev/null @@ -1,27 +0,0 @@ -use core::{mem, ptr}; - -use crate::sys::c; - -#[cfg(not(target_vendor = "win7"))] -#[inline] -pub fn hashmap_random_keys() -> (u64, u64) { - let mut v = (0, 0); - let ret = unsafe { c::ProcessPrng(ptr::addr_of_mut!(v).cast::<u8>(), mem::size_of_val(&v)) }; - // ProcessPrng is documented as always returning `TRUE`. - // https://learn.microsoft.com/en-us/windows/win32/seccng/processprng#return-value - debug_assert_eq!(ret, c::TRUE); - v -} - -#[cfg(target_vendor = "win7")] -pub fn hashmap_random_keys() -> (u64, u64) { - use crate::ffi::c_void; - use crate::io; - - let mut v = (0, 0); - let ret = unsafe { - c::RtlGenRandom(ptr::addr_of_mut!(v).cast::<c_void>(), mem::size_of_val(&v) as u32) - }; - - if ret != 0 { v } else { panic!("RNG broken: {}", io::Error::last_os_error()) } -} diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index 961d45c5e83..b211e94db65 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -1,6 +1,5 @@ #![forbid(unsafe_op_in_unsafe_fn)] -pub mod alloc; #[path = "../unsupported/args.rs"] pub mod args; #[path = "../unsupported/env.rs"] diff --git a/library/std/src/sys/pal/xous/net/dns.rs b/library/std/src/sys/pal/xous/net/dns.rs index 50efe978c4a..d0083c61837 100644 --- a/library/std/src/sys/pal/xous/net/dns.rs +++ b/library/std/src/sys/pal/xous/net/dns.rs @@ -3,7 +3,7 @@ use core::convert::{TryFrom, TryInto}; use crate::io; use crate::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::os::xous::ffi::lend_mut; -use crate::os::xous::services::{dns_server, DnsLendMut}; +use crate::os::xous::services::{DnsLendMut, dns_server}; pub struct DnsError { pub code: u8, diff --git a/library/std/src/sys/pal/xous/stdio.rs b/library/std/src/sys/pal/xous/stdio.rs index 11608964b52..dfd47a1775a 100644 --- a/library/std/src/sys/pal/xous/stdio.rs +++ b/library/std/src/sys/pal/xous/stdio.rs @@ -4,8 +4,8 @@ pub struct Stdin; pub struct Stdout {} pub struct Stderr; -use crate::os::xous::ffi::{lend, try_lend, try_scalar, Connection}; -use crate::os::xous::services::{log_server, try_connect, LogLend, LogScalar}; +use crate::os::xous::ffi::{Connection, lend, try_lend, try_scalar}; +use crate::os::xous::services::{LogLend, LogScalar, log_server, try_connect}; impl Stdin { pub const fn new() -> Stdin { diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs index a95b0aa14d2..0ebb46dc19f 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/pal/xous/thread.rs @@ -4,10 +4,10 @@ use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::os::xous::ffi::{ - blocking_scalar, create_thread, do_yield, join_thread, map_memory, update_memory_flags, - MemoryFlags, Syscall, ThreadId, + MemoryFlags, Syscall, ThreadId, blocking_scalar, create_thread, do_yield, join_thread, + map_memory, update_memory_flags, }; -use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; +use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; use crate::time::Duration; pub struct Thread { diff --git a/library/std/src/sys/pal/zkvm/args.rs b/library/std/src/sys/pal/zkvm/args.rs index 583c16e3a47..47857f6c448 100644 --- a/library/std/src/sys/pal/zkvm/args.rs +++ b/library/std/src/sys/pal/zkvm/args.rs @@ -1,4 +1,4 @@ -use super::{abi, WORD_SIZE}; +use super::{WORD_SIZE, abi}; use crate::ffi::OsString; use crate::fmt; use crate::sys::os_str; diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 651f25d6623..6ea05772029 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -10,7 +10,7 @@ const WORD_SIZE: usize = core::mem::size_of::<u32>(); -pub mod alloc; +pub mod abi; #[path = "../zkvm/args.rs"] pub mod args; pub mod env; @@ -26,13 +26,10 @@ pub mod pipe; #[path = "../unsupported/process.rs"] pub mod process; pub mod stdio; -#[path = "../unsupported/time.rs"] -pub mod time; - #[path = "../unsupported/thread.rs"] pub mod thread; - -mod abi; +#[path = "../unsupported/time.rs"] +pub mod time; use crate::io as std_io; @@ -63,11 +60,3 @@ pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { pub fn abort_internal() -> ! { core::intrinsics::abort(); } - -pub fn hashmap_random_keys() -> (u64, u64) { - let mut buf = [0u32; 4]; - unsafe { - abi::sys_rand(buf.as_mut_ptr(), 4); - }; - ((buf[0] as u64) << 32 + buf[1] as u64, (buf[2] as u64) << 32 + buf[3] as u64) -} diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index 68d91a123ac..5d224ffd1ba 100644 --- a/library/std/src/sys/pal/zkvm/os.rs +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -1,4 +1,4 @@ -use super::{abi, unsupported, WORD_SIZE}; +use super::{WORD_SIZE, abi, unsupported}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; diff --git a/library/std/src/sys/path/windows.rs b/library/std/src/sys/path/windows.rs index 21841eb18cc..9267602cb97 100644 --- a/library/std/src/sys/path/windows.rs +++ b/library/std/src/sys/path/windows.rs @@ -1,5 +1,6 @@ use crate::ffi::{OsStr, OsString}; use crate::path::{Path, PathBuf, Prefix}; +use crate::sys::api::utf16; use crate::sys::pal::{c, fill_utf16_buf, os2path, to_u16s}; use crate::{io, ptr}; @@ -19,6 +20,10 @@ pub fn is_verbatim_sep(b: u8) -> bool { b == b'\\' } +pub fn is_verbatim(path: &[u16]) -> bool { + path.starts_with(utf16!(r"\\?\")) || path.starts_with(utf16!(r"\??\")) +} + /// Returns true if `path` looks like a lone filename. pub(crate) fn is_file_name(path: &OsStr) -> bool { !path.as_encoded_bytes().iter().copied().any(is_sep_byte) @@ -94,7 +99,7 @@ impl<'a> PrefixParserSlice<'a, '_> { } pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> { - use Prefix::{DeviceNS, Disk, Verbatim, VerbatimDisk, VerbatimUNC, UNC}; + use Prefix::{DeviceNS, Disk, UNC, Verbatim, VerbatimDisk, VerbatimUNC}; let parser = PrefixParser::<8>::new(path); let parser = parser.as_slice(); diff --git a/library/std/src/sys/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs index c37c3e442ae..778d8686f02 100644 --- a/library/std/src/sys/personality/dwarf/eh.rs +++ b/library/std/src/sys/personality/dwarf/eh.rs @@ -54,10 +54,10 @@ pub enum EHAction { Terminate, } -/// 32-bit Apple ARM uses SjLj exceptions, except for watchOS. +/// 32-bit ARM Darwin platforms uses SjLj exceptions. /// -/// I.e. iOS and tvOS, as those are the only Apple OSes that used 32-bit ARM -/// devices. +/// The exception is watchOS armv7k (specifically that subarchitecture), which +/// instead uses DWARF Call Frame Information (CFI) unwinding. /// /// <https://github.com/llvm/llvm-project/blob/llvmorg-18.1.4/clang/lib/Driver/ToolChains/Darwin.cpp#L3107-L3119> pub const USING_SJLJ_EXCEPTIONS: bool = diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index f6b1844e153..ad596ecff65 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -95,14 +95,15 @@ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 cfg_if::cfg_if! { if #[cfg(all( - target_arch = "arm", - not(all(target_vendor = "apple", not(target_os = "watchos"))), - not(target_os = "netbsd"), - ))] { + target_arch = "arm", + not(target_vendor = "apple"), + not(target_os = "netbsd"), + ))] { /// personality fn called by [ARM EHABI][armeabi-eh] /// - /// Apple 32-bit ARM (but not watchOS) uses the default routine instead - /// since it uses "setjmp-longjmp" unwinding. + /// 32-bit ARM on iOS/tvOS/watchOS does not use ARM EHABI, it uses + /// either "setjmp-longjmp" unwinding or DWARF CFI unwinding, which is + /// handled by the default routine. /// /// [armeabi-eh]: https://web.archive.org/web/20190728160938/https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf #[lang = "eh_personality"] diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 1a6ea1dafcb..68085d026c4 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -31,7 +31,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re")), + all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems")), all(target_vendor = "fortanix", target_env = "sgx"), ))] { mod gcc; diff --git a/library/std/src/sys/random/apple.rs b/library/std/src/sys/random/apple.rs new file mode 100644 index 00000000000..417198c9d85 --- /dev/null +++ b/library/std/src/sys/random/apple.rs @@ -0,0 +1,15 @@ +//! Random data on Apple platforms. +//! +//! `CCRandomGenerateBytes` calls into `CCRandomCopyBytes` with `kCCRandomDefault`. +//! `CCRandomCopyBytes` manages a CSPRNG which is seeded from the kernel's CSPRNG. +//! We use `CCRandomGenerateBytes` instead of `SecCopyBytes` because it is accessible via +//! `libSystem` (libc) while the other needs to link to `Security.framework`. +//! +//! Note that technically, `arc4random_buf` is available as well, but that calls +//! into the same system service anyway, and `CCRandomGenerateBytes` has been +//! proven to be App Store-compatible. + +pub fn fill_bytes(bytes: &mut [u8]) { + let ret = unsafe { libc::CCRandomGenerateBytes(bytes.as_mut_ptr().cast(), bytes.len()) }; + assert_eq!(ret, libc::kCCSuccess, "failed to generate random data"); +} diff --git a/library/std/src/sys/random/arc4random.rs b/library/std/src/sys/random/arc4random.rs new file mode 100644 index 00000000000..32467e9ebaa --- /dev/null +++ b/library/std/src/sys/random/arc4random.rs @@ -0,0 +1,34 @@ +//! Random data generation with `arc4random_buf`. +//! +//! Contrary to its name, `arc4random` doesn't actually use the horribly-broken +//! RC4 cypher anymore, at least not on modern systems, but rather something +//! like ChaCha20 with continual reseeding from the OS. That makes it an ideal +//! source of large quantities of cryptographically secure data, which is exactly +//! what we need for `DefaultRandomSource`. Unfortunately, it's not available +//! on all UNIX systems, most notably Linux (until recently, but it's just a +//! wrapper for `getrandom`. Since we need to hook into `getrandom` directly +//! for `HashMap` keys anyway, we just keep our version). + +#[cfg(not(any( + target_os = "haiku", + target_os = "illumos", + target_os = "solaris", + target_os = "vita", +)))] +use libc::arc4random_buf; + +// FIXME: move these to libc (except Haiku, that one needs to link to libbsd.so). +#[cfg(any( + target_os = "haiku", // See https://git.haiku-os.org/haiku/tree/headers/compatibility/bsd/stdlib.h + target_os = "illumos", // See https://www.illumos.org/man/3C/arc4random + target_os = "solaris", // See https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html + target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74 +))] +#[cfg_attr(target_os = "haiku", link(name = "bsd"))] +extern "C" { + fn arc4random_buf(buf: *mut core::ffi::c_void, nbytes: libc::size_t); +} + +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { arc4random_buf(bytes.as_mut_ptr().cast(), bytes.len()) } +} diff --git a/library/std/src/sys/random/espidf.rs b/library/std/src/sys/random/espidf.rs new file mode 100644 index 00000000000..fd52cb5559c --- /dev/null +++ b/library/std/src/sys/random/espidf.rs @@ -0,0 +1,9 @@ +use crate::ffi::c_void; + +extern "C" { + fn esp_fill_random(buf: *mut c_void, len: usize); +} + +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { esp_fill_random(bytes.as_mut_ptr().cast(), bytes.len()) } +} diff --git a/library/std/src/sys/random/fuchsia.rs b/library/std/src/sys/random/fuchsia.rs new file mode 100644 index 00000000000..77d72b3c5b7 --- /dev/null +++ b/library/std/src/sys/random/fuchsia.rs @@ -0,0 +1,13 @@ +//! Random data generation using the Zircon kernel. +//! +//! Fuchsia, as always, is quite nice and provides exactly the API we need: +//! <https://fuchsia.dev/reference/syscalls/cprng_draw>. + +#[link(name = "zircon")] +extern "C" { + fn zx_cprng_draw(buffer: *mut u8, len: usize); +} + +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { zx_cprng_draw(bytes.as_mut_ptr(), bytes.len()) } +} diff --git a/library/std/src/sys/random/getentropy.rs b/library/std/src/sys/random/getentropy.rs new file mode 100644 index 00000000000..110ac134c1f --- /dev/null +++ b/library/std/src/sys/random/getentropy.rs @@ -0,0 +1,17 @@ +//! Random data generation through `getentropy`. +//! +//! Since issue 8 (2024), the POSIX specification mandates the existence of the +//! `getentropy` function, which fills a slice of up to `GETENTROPY_MAX` bytes +//! (256 on all known platforms) with random data. Unfortunately, it's only +//! meant to be used to seed other CPRNGs, which we don't have, so we only use +//! it where `arc4random_buf` and friends aren't available or secure (currently +//! that's only the case on Emscripten). + +pub fn fill_bytes(bytes: &mut [u8]) { + // GETENTROPY_MAX isn't defined yet on most platforms, but it's mandated + // to be at least 256, so just use that as limit. + for chunk in bytes.chunks_mut(256) { + let r = unsafe { libc::getentropy(chunk.as_mut_ptr().cast(), chunk.len()) }; + assert_ne!(r, -1, "failed to generate random data"); + } +} diff --git a/library/std/src/sys/random/hermit.rs b/library/std/src/sys/random/hermit.rs new file mode 100644 index 00000000000..92c0550d2d5 --- /dev/null +++ b/library/std/src/sys/random/hermit.rs @@ -0,0 +1,7 @@ +pub fn fill_bytes(mut bytes: &mut [u8]) { + while !bytes.is_empty() { + let res = unsafe { hermit_abi::read_entropy(bytes.as_mut_ptr(), bytes.len(), 0) }; + assert_ne!(res, -1, "failed to generate random data"); + bytes = &mut bytes[res as usize..]; + } +} diff --git a/library/std/src/sys/random/horizon.rs b/library/std/src/sys/random/horizon.rs new file mode 100644 index 00000000000..0be2eae20a7 --- /dev/null +++ b/library/std/src/sys/random/horizon.rs @@ -0,0 +1,7 @@ +pub fn fill_bytes(mut bytes: &mut [u8]) { + while !bytes.is_empty() { + let r = unsafe { libc::getrandom(bytes.as_mut_ptr().cast(), bytes.len(), 0) }; + assert_ne!(r, -1, "failed to generate random data"); + bytes = &mut bytes[r as usize..]; + } +} diff --git a/library/std/src/sys/random/linux.rs b/library/std/src/sys/random/linux.rs new file mode 100644 index 00000000000..073fdc45e61 --- /dev/null +++ b/library/std/src/sys/random/linux.rs @@ -0,0 +1,170 @@ +//! Random data generation with the Linux kernel. +//! +//! The first interface random data interface to be introduced on Linux were +//! the `/dev/random` and `/dev/urandom` special files. As paths can become +//! unreachable when inside a chroot and when the file descriptors are exhausted, +//! this was not enough to provide userspace with a reliable source of randomness, +//! so when the OpenBSD 5.6 introduced the `getentropy` syscall, Linux 3.17 got +//! its very own `getrandom` syscall to match.[^1] Unfortunately, even if our +//! minimum supported version were high enough, we still couldn't rely on the +//! syscall being available, as it is blocked in `seccomp` by default. +//! +//! The question is therefore which of the random sources to use. Historically, +//! the kernel contained two pools: the blocking and non-blocking pool. The +//! blocking pool used entropy estimation to limit the amount of available +//! bytes, while the non-blocking pool, once initialized using the blocking +//! pool, uses a CPRNG to return an unlimited number of random bytes. With a +//! strong enough CPRNG however, the entropy estimation didn't contribute that +//! much towards security while being an excellent vector for DoS attacs. Thus, +//! the blocking pool was removed in kernel version 5.6.[^2] That patch did not +//! magically increase the quality of the non-blocking pool, however, so we can +//! safely consider it strong enough even in older kernel versions and use it +//! unconditionally. +//! +//! One additional consideration to make is that the non-blocking pool is not +//! always initialized during early boot. We want the best quality of randomness +//! for the output of `DefaultRandomSource` so we simply wait until it is +//! initialized. When `HashMap` keys however, this represents a potential source +//! of deadlocks, as the additional entropy may only be generated once the +//! program makes forward progress. In that case, we just use the best random +//! data the system has available at the time. +//! +//! So in conclusion, we always want the output of the non-blocking pool, but +//! may need to wait until it is initalized. The default behaviour of `getrandom` +//! is to wait until the non-blocking pool is initialized and then draw from there, +//! so if `getrandom` is available, we use its default to generate the bytes. For +//! `HashMap`, however, we need to specify the `GRND_INSECURE` flags, but that +//! is only available starting with kernel version 5.6. Thus, if we detect that +//! the flag is unsupported, we try `GRND_NONBLOCK` instead, which will only +//! succeed if the pool is initialized. If it isn't, we fall back to the file +//! access method. +//! +//! The behaviour of `/dev/urandom` is inverse to that of `getrandom`: it always +//! yields data, even when the pool is not initialized. For generating `HashMap` +//! keys, this is not important, so we can use it directly. For secure data +//! however, we need to wait until initialization, which we can do by `poll`ing +//! `/dev/random`. +//! +//! TLDR: our fallback strategies are: +//! +//! Secure data | `HashMap` keys +//! --------------------------------------------|------------------ +//! getrandom(0) | getrandom(GRND_INSECURE) +//! poll("/dev/random") && read("/dev/urandom") | getrandom(GRND_NONBLOCK) +//! | read("/dev/urandom") +//! +//! [^1]: <https://lwn.net/Articles/606141/> +//! [^2]: <https://lwn.net/Articles/808575/> +//! +// FIXME(in 2040 or so): once the minimum kernel version is 5.6, remove the +// `GRND_NONBLOCK` fallback and use `/dev/random` instead of `/dev/urandom` +// when secure data is required. + +use crate::fs::File; +use crate::io::Read; +use crate::os::fd::AsRawFd; +use crate::sync::OnceLock; +use crate::sync::atomic::AtomicBool; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sys::pal::os::errno; +use crate::sys::pal::weak::syscall; + +fn getrandom(mut bytes: &mut [u8], insecure: bool) { + // A weak symbol allows interposition, e.g. for perf measurements that want to + // disable randomness for consistency. Otherwise, we'll try a raw syscall. + // (`getrandom` was added in glibc 2.25, musl 1.1.20, android API level 28) + syscall! { + fn getrandom( + buffer: *mut libc::c_void, + length: libc::size_t, + flags: libc::c_uint + ) -> libc::ssize_t + } + + static GETRANDOM_AVAILABLE: AtomicBool = AtomicBool::new(true); + static GRND_INSECURE_AVAILABLE: AtomicBool = AtomicBool::new(true); + static URANDOM_READY: AtomicBool = AtomicBool::new(false); + static DEVICE: OnceLock<File> = OnceLock::new(); + + if GETRANDOM_AVAILABLE.load(Relaxed) { + loop { + if bytes.is_empty() { + return; + } + + let flags = if insecure { + if GRND_INSECURE_AVAILABLE.load(Relaxed) { + libc::GRND_INSECURE + } else { + libc::GRND_NONBLOCK + } + } else { + 0 + }; + + let ret = unsafe { getrandom(bytes.as_mut_ptr().cast(), bytes.len(), flags) }; + if ret != -1 { + bytes = &mut bytes[ret as usize..]; + } else { + match errno() { + libc::EINTR => continue, + // `GRND_INSECURE` is not available, try + // `GRND_NONBLOCK`. + libc::EINVAL if flags == libc::GRND_INSECURE => { + GRND_INSECURE_AVAILABLE.store(false, Relaxed); + continue; + } + // The pool is not initialized yet, fall back to + // /dev/urandom for now. + libc::EAGAIN if flags == libc::GRND_NONBLOCK => break, + // `getrandom` is unavailable or blocked by seccomp. + // Don't try it again and fall back to /dev/urandom. + libc::ENOSYS | libc::EPERM => { + GETRANDOM_AVAILABLE.store(false, Relaxed); + break; + } + _ => panic!("failed to generate random data"), + } + } + } + } + + // When we want cryptographic strength, we need to wait for the CPRNG-pool + // to become initialized. Do this by polling `/dev/random` until it is ready. + if !insecure { + if !URANDOM_READY.load(Acquire) { + let random = File::open("/dev/random").expect("failed to open /dev/random"); + let mut fd = libc::pollfd { fd: random.as_raw_fd(), events: libc::POLLIN, revents: 0 }; + + while !URANDOM_READY.load(Acquire) { + let ret = unsafe { libc::poll(&mut fd, 1, -1) }; + match ret { + 1 => { + assert_eq!(fd.revents, libc::POLLIN); + URANDOM_READY.store(true, Release); + break; + } + -1 if errno() == libc::EINTR => continue, + _ => panic!("poll(\"/dev/random\") failed"), + } + } + } + } + + DEVICE + .get_or_try_init(|| File::open("/dev/urandom")) + .and_then(|mut dev| dev.read_exact(bytes)) + .expect("failed to generate random data"); +} + +pub fn fill_bytes(bytes: &mut [u8]) { + getrandom(bytes, false); +} + +pub fn hashmap_random_keys() -> (u64, u64) { + let mut bytes = [0; 16]; + getrandom(&mut bytes, true); + let k1 = u64::from_ne_bytes(bytes[..8].try_into().unwrap()); + let k2 = u64::from_ne_bytes(bytes[8..].try_into().unwrap()); + (k1, k2) +} diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs new file mode 100644 index 00000000000..16fb8c64c9b --- /dev/null +++ b/library/std/src/sys/random/mod.rs @@ -0,0 +1,95 @@ +cfg_if::cfg_if! { + // Tier 1 + if #[cfg(any(target_os = "linux", target_os = "android"))] { + mod linux; + pub use linux::{fill_bytes, hashmap_random_keys}; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::fill_bytes; + } else if #[cfg(target_vendor = "apple")] { + mod apple; + pub use apple::fill_bytes; + // Others, in alphabetical ordering. + } else if #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "haiku", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "vita", + ))] { + mod arc4random; + pub use arc4random::fill_bytes; + } else if #[cfg(target_os = "emscripten")] { + mod getentropy; + pub use getentropy::fill_bytes; + } else if #[cfg(target_os = "espidf")] { + mod espidf; + pub use espidf::fill_bytes; + } else if #[cfg(target_os = "fuchsia")] { + mod fuchsia; + pub use fuchsia::fill_bytes; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::fill_bytes; + } else if #[cfg(target_os = "horizon")] { + // FIXME: add arc4random_buf to shim-3ds + mod horizon; + pub use horizon::fill_bytes; + } else if #[cfg(any( + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + ))] { + mod unix_legacy; + pub use unix_legacy::fill_bytes; + } else if #[cfg(target_os = "redox")] { + mod redox; + pub use redox::fill_bytes; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod sgx; + pub use sgx::fill_bytes; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::fill_bytes; + } else if #[cfg(target_os = "teeos")] { + mod teeos; + pub use teeos::fill_bytes; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::fill_bytes; + } else if #[cfg(target_os = "vxworks")] { + mod vxworks; + pub use vxworks::fill_bytes; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use wasi::fill_bytes; + } else if #[cfg(target_os = "zkvm")] { + mod zkvm; + pub use zkvm::fill_bytes; + } else if #[cfg(any( + all(target_family = "wasm", target_os = "unknown"), + target_os = "xous", + ))] { + // FIXME: finally remove std support for wasm32-unknown-unknown + // FIXME: add random data generation to xous + mod unsupported; + pub use unsupported::{fill_bytes, hashmap_random_keys}; + } +} + +#[cfg(not(any( + target_os = "linux", + target_os = "android", + all(target_family = "wasm", target_os = "unknown"), + target_os = "xous", +)))] +pub fn hashmap_random_keys() -> (u64, u64) { + let mut buf = [0; 16]; + fill_bytes(&mut buf); + let k1 = u64::from_ne_bytes(buf[..8].try_into().unwrap()); + let k2 = u64::from_ne_bytes(buf[8..].try_into().unwrap()); + (k1, k2) +} diff --git a/library/std/src/sys/random/redox.rs b/library/std/src/sys/random/redox.rs new file mode 100644 index 00000000000..b004335a351 --- /dev/null +++ b/library/std/src/sys/random/redox.rs @@ -0,0 +1,12 @@ +use crate::fs::File; +use crate::io::Read; +use crate::sync::OnceLock; + +static SCHEME: OnceLock<File> = OnceLock::new(); + +pub fn fill_bytes(bytes: &mut [u8]) { + SCHEME + .get_or_try_init(|| File::open("/scheme/rand")) + .and_then(|mut scheme| scheme.read_exact(bytes)) + .expect("failed to generate random data"); +} diff --git a/library/std/src/sys/random/sgx.rs b/library/std/src/sys/random/sgx.rs new file mode 100644 index 00000000000..c3647a8df22 --- /dev/null +++ b/library/std/src/sys/random/sgx.rs @@ -0,0 +1,67 @@ +use crate::arch::x86_64::{_rdrand16_step, _rdrand32_step, _rdrand64_step}; + +const RETRIES: u32 = 10; + +fn fail() -> ! { + panic!("failed to generate random data"); +} + +fn rdrand64() -> u64 { + unsafe { + let mut ret: u64 = 0; + for _ in 0..RETRIES { + if _rdrand64_step(&mut ret) == 1 { + return ret; + } + } + + fail(); + } +} + +fn rdrand32() -> u32 { + unsafe { + let mut ret: u32 = 0; + for _ in 0..RETRIES { + if _rdrand32_step(&mut ret) == 1 { + return ret; + } + } + + fail(); + } +} + +fn rdrand16() -> u16 { + unsafe { + let mut ret: u16 = 0; + for _ in 0..RETRIES { + if _rdrand16_step(&mut ret) == 1 { + return ret; + } + } + + fail(); + } +} + +pub fn fill_bytes(bytes: &mut [u8]) { + let mut chunks = bytes.array_chunks_mut(); + for chunk in &mut chunks { + *chunk = rdrand64().to_ne_bytes(); + } + + let mut chunks = chunks.into_remainder().array_chunks_mut(); + for chunk in &mut chunks { + *chunk = rdrand32().to_ne_bytes(); + } + + let mut chunks = chunks.into_remainder().array_chunks_mut(); + for chunk in &mut chunks { + *chunk = rdrand16().to_ne_bytes(); + } + + if let [byte] = chunks.into_remainder() { + *byte = rdrand16() as u8; + } +} diff --git a/library/std/src/sys/random/solid.rs b/library/std/src/sys/random/solid.rs new file mode 100644 index 00000000000..545771150e2 --- /dev/null +++ b/library/std/src/sys/random/solid.rs @@ -0,0 +1,8 @@ +use crate::sys::pal::abi; + +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { + let result = abi::SOLID_RNG_SampleRandomBytes(bytes.as_mut_ptr(), bytes.len()); + assert_eq!(result, 0, "failed to generate random data"); + } +} diff --git a/library/std/src/sys/random/teeos.rs b/library/std/src/sys/random/teeos.rs new file mode 100644 index 00000000000..fd6b24e19e9 --- /dev/null +++ b/library/std/src/sys/random/teeos.rs @@ -0,0 +1,7 @@ +extern "C" { + fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t); +} + +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { TEE_GenerateRandom(bytes.as_mut_ptr().cast(), bytes.len()) } +} diff --git a/library/std/src/sys/random/uefi.rs b/library/std/src/sys/random/uefi.rs new file mode 100644 index 00000000000..a4d29e66f38 --- /dev/null +++ b/library/std/src/sys/random/uefi.rs @@ -0,0 +1,27 @@ +use r_efi::protocols::rng; + +use crate::sys::pal::helpers; + +pub fn fill_bytes(bytes: &mut [u8]) { + let handles = + helpers::locate_handles(rng::PROTOCOL_GUID).expect("failed to generate random data"); + for handle in handles { + if let Ok(protocol) = helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) { + let r = unsafe { + ((*protocol.as_ptr()).get_rng)( + protocol.as_ptr(), + crate::ptr::null_mut(), + bytes.len(), + bytes.as_mut_ptr(), + ) + }; + if r.is_error() { + continue; + } else { + return; + } + } + } + + panic!("failed to generate random data"); +} diff --git a/library/std/src/sys/random/unix_legacy.rs b/library/std/src/sys/random/unix_legacy.rs new file mode 100644 index 00000000000..587068b0d66 --- /dev/null +++ b/library/std/src/sys/random/unix_legacy.rs @@ -0,0 +1,20 @@ +//! Random data from `/dev/urandom` +//! +//! Before `getentropy` was standardized in 2024, UNIX didn't have a standardized +//! way of getting random data, so systems just followed the precedent set by +//! Linux and exposed random devices at `/dev/random` and `/dev/urandom`. Thus, +//! for the few systems that support neither `arc4random_buf` nor `getentropy` +//! yet, we just read from the file. + +use crate::fs::File; +use crate::io::Read; +use crate::sync::OnceLock; + +static DEVICE: OnceLock<File> = OnceLock::new(); + +pub fn fill_bytes(bytes: &mut [u8]) { + DEVICE + .get_or_try_init(|| File::open("/dev/urandom")) + .and_then(|mut dev| dev.read_exact(bytes)) + .expect("failed to generate random data"); +} diff --git a/library/std/src/sys/random/unsupported.rs b/library/std/src/sys/random/unsupported.rs new file mode 100644 index 00000000000..d68ce4a9e87 --- /dev/null +++ b/library/std/src/sys/random/unsupported.rs @@ -0,0 +1,15 @@ +use crate::ptr; + +pub fn fill_bytes(_: &mut [u8]) { + panic!("this target does not support random data generation"); +} + +pub fn hashmap_random_keys() -> (u64, u64) { + // Use allocation addresses for a bit of randomness. This isn't + // particularily secure, but there isn't really an alternative. + let stack = 0u8; + let heap = Box::new(0u8); + let k1 = ptr::from_ref(&stack).addr() as u64; + let k2 = ptr::from_ref(&*heap).addr() as u64; + (k1, k2) +} diff --git a/library/std/src/sys/random/vxworks.rs b/library/std/src/sys/random/vxworks.rs new file mode 100644 index 00000000000..d549ccebdb2 --- /dev/null +++ b/library/std/src/sys/random/vxworks.rs @@ -0,0 +1,25 @@ +use crate::sync::atomic::AtomicBool; +use crate::sync::atomic::Ordering::Relaxed; + +static RNG_INIT: AtomicBool = AtomicBool::new(false); + +pub fn fill_bytes(mut bytes: &mut [u8]) { + while !RNG_INIT.load(Relaxed) { + let ret = unsafe { libc::randSecure() }; + if ret < 0 { + panic!("failed to generate random data"); + } else if ret > 0 { + RNG_INIT.store(true, Relaxed); + break; + } + + unsafe { libc::usleep(10) }; + } + + while !bytes.is_empty() { + let len = bytes.len().try_into().unwrap_or(libc::c_int::MAX); + let ret = unsafe { libc::randABytes(bytes.as_mut_ptr(), len) }; + assert!(ret >= 0, "failed to generate random data"); + bytes = &mut bytes[len as usize..]; + } +} diff --git a/library/std/src/sys/random/wasi.rs b/library/std/src/sys/random/wasi.rs new file mode 100644 index 00000000000..d41da3751fc --- /dev/null +++ b/library/std/src/sys/random/wasi.rs @@ -0,0 +1,5 @@ +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { + wasi::random_get(bytes.as_mut_ptr(), bytes.len()).expect("failed to generate random data") + } +} diff --git a/library/std/src/sys/random/windows.rs b/library/std/src/sys/random/windows.rs new file mode 100644 index 00000000000..7566000f9e6 --- /dev/null +++ b/library/std/src/sys/random/windows.rs @@ -0,0 +1,20 @@ +use crate::sys::c; + +#[cfg(not(target_vendor = "win7"))] +#[inline] +pub fn fill_bytes(bytes: &mut [u8]) { + let ret = unsafe { c::ProcessPrng(bytes.as_mut_ptr(), bytes.len()) }; + // ProcessPrng is documented as always returning `TRUE`. + // https://learn.microsoft.com/en-us/windows/win32/seccng/processprng#return-value + debug_assert_eq!(ret, c::TRUE); +} + +#[cfg(target_vendor = "win7")] +pub fn fill_bytes(mut bytes: &mut [u8]) { + while !bytes.is_empty() { + let len = bytes.len().try_into().unwrap_or(u32::MAX); + let ret = unsafe { c::RtlGenRandom(bytes.as_mut_ptr().cast(), len) }; + assert_ne!(ret, 0, "failed to generate random data"); + bytes = &mut bytes[len as usize..]; + } +} diff --git a/library/std/src/sys/random/zkvm.rs b/library/std/src/sys/random/zkvm.rs new file mode 100644 index 00000000000..3011942f6b2 --- /dev/null +++ b/library/std/src/sys/random/zkvm.rs @@ -0,0 +1,21 @@ +use crate::sys::pal::abi; + +pub fn fill_bytes(bytes: &mut [u8]) { + let (pre, words, post) = unsafe { bytes.align_to_mut::<u32>() }; + if !words.is_empty() { + unsafe { + abi::sys_rand(words.as_mut_ptr(), words.len()); + } + } + + let mut buf = [0u32; 2]; + let len = (pre.len() + post.len() + size_of::<u32>() - 1) / size_of::<u32>(); + if len != 0 { + unsafe { abi::sys_rand(buf.as_mut_ptr(), len) }; + } + + let buf = buf.map(u32::to_ne_bytes); + let buf = buf.as_flattened(); + pre.copy_from_slice(&buf[..pre.len()]); + post.copy_from_slice(&buf[pre.len()..pre.len() + post.len()]); +} diff --git a/library/std/src/sys/sync/condvar/pthread.rs b/library/std/src/sys/sync/condvar/pthread.rs index 2b4bdfe415c..5b5e7770b06 100644 --- a/library/std/src/sys/sync/condvar/pthread.rs +++ b/library/std/src/sys/sync/condvar/pthread.rs @@ -2,7 +2,7 @@ use crate::cell::UnsafeCell; use crate::ptr; use crate::sync::atomic::AtomicPtr; use crate::sync::atomic::Ordering::Relaxed; -use crate::sys::sync::{mutex, Mutex}; +use crate::sys::sync::{Mutex, mutex}; #[cfg(not(target_os = "nto"))] use crate::sys::time::TIMESPEC_MAX; #[cfg(target_os = "nto")] diff --git a/library/std/src/sys/sync/condvar/windows7.rs b/library/std/src/sys/sync/condvar/windows7.rs index 56eeeda551e..f03feef2221 100644 --- a/library/std/src/sys/sync/condvar/windows7.rs +++ b/library/std/src/sys/sync/condvar/windows7.rs @@ -1,5 +1,5 @@ use crate::cell::UnsafeCell; -use crate::sys::sync::{mutex, Mutex}; +use crate::sys::sync::{Mutex, mutex}; use crate::sys::{c, os}; use crate::time::Duration; diff --git a/library/std/src/sys/sync/condvar/xous.rs b/library/std/src/sys/sync/condvar/xous.rs index 007383479a1..cb55a3e3369 100644 --- a/library/std/src/sys/sync/condvar/xous.rs +++ b/library/std/src/sys/sync/condvar/xous.rs @@ -1,7 +1,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use crate::os::xous::ffi::{blocking_scalar, scalar}; -use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; +use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; use crate::sys::sync::Mutex; use crate::time::Duration; diff --git a/library/std/src/sys/sync/mutex/fuchsia.rs b/library/std/src/sys/sync/mutex/fuchsia.rs index 81a6361a83a..3e871285bea 100644 --- a/library/std/src/sys/sync/mutex/fuchsia.rs +++ b/library/std/src/sys/sync/mutex/fuchsia.rs @@ -40,9 +40,9 @@ use crate::sync::atomic::AtomicU32; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sys::futex::zircon::{ - zx_futex_wait, zx_futex_wake_single_owner, zx_handle_t, zx_thread_self, ZX_ERR_BAD_HANDLE, - ZX_ERR_BAD_STATE, ZX_ERR_INVALID_ARGS, ZX_ERR_TIMED_OUT, ZX_ERR_WRONG_TYPE, ZX_OK, - ZX_TIME_INFINITE, + ZX_ERR_BAD_HANDLE, ZX_ERR_BAD_STATE, ZX_ERR_INVALID_ARGS, ZX_ERR_TIMED_OUT, ZX_ERR_WRONG_TYPE, + ZX_OK, ZX_TIME_INFINITE, zx_futex_wait, zx_futex_wake_single_owner, zx_handle_t, + zx_thread_self, }; // The lowest two bits of a `zx_handle_t` are always set, so the lowest bit is used to mark the diff --git a/library/std/src/sys/sync/mutex/itron.rs b/library/std/src/sys/sync/mutex/itron.rs index 8440ffdd337..dcdfff0c81c 100644 --- a/library/std/src/sys/sync/mutex/itron.rs +++ b/library/std/src/sys/sync/mutex/itron.rs @@ -3,7 +3,7 @@ #![forbid(unsafe_op_in_unsafe_fn)] use crate::sys::pal::itron::abi; -use crate::sys::pal::itron::error::{expect_success, expect_success_aborting, fail, ItronError}; +use crate::sys::pal::itron::error::{ItronError, expect_success, expect_success_aborting, fail}; use crate::sys::pal::itron::spin::SpinIdOnceCell; pub struct Mutex { diff --git a/library/std/src/sys/sync/mutex/pthread.rs b/library/std/src/sys/sync/mutex/pthread.rs index ee0794334fb..1c407bc2537 100644 --- a/library/std/src/sys/sync/mutex/pthread.rs +++ b/library/std/src/sys/sync/mutex/pthread.rs @@ -1,6 +1,6 @@ use crate::cell::UnsafeCell; use crate::io::Error; -use crate::mem::{forget, MaybeUninit}; +use crate::mem::{MaybeUninit, forget}; use crate::sys::cvt_nz; use crate::sys_common::lazy_box::{LazyBox, LazyInit}; diff --git a/library/std/src/sys/sync/mutex/sgx.rs b/library/std/src/sys/sync/mutex/sgx.rs index d37bd02adf8..65d1e880f7b 100644 --- a/library/std/src/sys/sync/mutex/sgx.rs +++ b/library/std/src/sys/sync/mutex/sgx.rs @@ -1,4 +1,4 @@ -use crate::sys::pal::waitqueue::{try_lock_or_false, SpinMutex, WaitQueue, WaitVariable}; +use crate::sys::pal::waitqueue::{SpinMutex, WaitQueue, WaitVariable, try_lock_or_false}; use crate::sys_common::lazy_box::{LazyBox, LazyInit}; /// FIXME: `UnsafeList` is not movable. diff --git a/library/std/src/sys/sync/mutex/xous.rs b/library/std/src/sys/sync/mutex/xous.rs index 63efa5a0210..233e638f913 100644 --- a/library/std/src/sys/sync/mutex/xous.rs +++ b/library/std/src/sys/sync/mutex/xous.rs @@ -1,5 +1,5 @@ use crate::os::xous::ffi::{blocking_scalar, do_yield}; -use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; +use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sync::atomic::{AtomicBool, AtomicUsize}; diff --git a/library/std/src/sys/sync/once/futex.rs b/library/std/src/sys/sync/once/futex.rs index 2c8a054282b..25588a4217b 100644 --- a/library/std/src/sys/sync/once/futex.rs +++ b/library/std/src/sys/sync/once/futex.rs @@ -91,6 +91,15 @@ impl Once { } } + #[inline] + pub(crate) fn set_state(&mut self, new_state: ExclusiveState) { + *self.state_and_queued.get_mut() = match new_state { + ExclusiveState::Incomplete => INCOMPLETE, + ExclusiveState::Poisoned => POISONED, + ExclusiveState::Complete => COMPLETE, + }; + } + #[cold] #[track_caller] pub fn wait(&self, ignore_poisoning: bool) { diff --git a/library/std/src/sys/sync/once/no_threads.rs b/library/std/src/sys/sync/once/no_threads.rs index 12c1d9f5a6c..cdcffe790f5 100644 --- a/library/std/src/sys/sync/once/no_threads.rs +++ b/library/std/src/sys/sync/once/no_threads.rs @@ -55,6 +55,15 @@ impl Once { } } + #[inline] + pub(crate) fn set_state(&mut self, new_state: ExclusiveState) { + self.state.set(match new_state { + ExclusiveState::Incomplete => State::Incomplete, + ExclusiveState::Poisoned => State::Poisoned, + ExclusiveState::Complete => State::Complete, + }); + } + #[cold] #[track_caller] pub fn wait(&self, _ignore_poisoning: bool) { diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs index 86f72c82008..17abaf0bf26 100644 --- a/library/std/src/sys/sync/once/queue.rs +++ b/library/std/src/sys/sync/once/queue.rs @@ -140,6 +140,15 @@ impl Once { } } + #[inline] + pub(crate) fn set_state(&mut self, new_state: ExclusiveState) { + *self.state_and_queue.get_mut() = match new_state { + ExclusiveState::Incomplete => ptr::without_provenance_mut(INCOMPLETE), + ExclusiveState::Poisoned => ptr::without_provenance_mut(POISONED), + ExclusiveState::Complete => ptr::without_provenance_mut(COMPLETE), + }; + } + #[cold] #[track_caller] pub fn wait(&self, ignore_poisoning: bool) { diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs index 458c16516bb..0e658328c2e 100644 --- a/library/std/src/sys/sync/rwlock/queue.rs +++ b/library/std/src/sys/sync/rwlock/queue.rs @@ -110,7 +110,7 @@ use crate::cell::OnceCell; use crate::hint::spin_loop; use crate::mem; -use crate::ptr::{self, null_mut, without_provenance_mut, NonNull}; +use crate::ptr::{self, NonNull, null_mut, without_provenance_mut}; use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release}; use crate::sync::atomic::{AtomicBool, AtomicPtr}; use crate::thread::{self, Thread}; diff --git a/library/std/src/sys/sync/rwlock/solid.rs b/library/std/src/sys/sync/rwlock/solid.rs index 05371402020..7703082f951 100644 --- a/library/std/src/sys/sync/rwlock/solid.rs +++ b/library/std/src/sys/sync/rwlock/solid.rs @@ -2,7 +2,7 @@ #![forbid(unsafe_op_in_unsafe_fn)] use crate::sys::pal::abi; -use crate::sys::pal::itron::error::{expect_success, expect_success_aborting, fail, ItronError}; +use crate::sys::pal::itron::error::{ItronError, expect_success, expect_success_aborting, fail}; use crate::sys::pal::itron::spin::SpinIdOnceCell; pub struct RwLock { diff --git a/library/std/src/sys/sync/thread_parking/id.rs b/library/std/src/sys/sync/thread_parking/id.rs index a7b07b509df..64964351837 100644 --- a/library/std/src/sys/sync/thread_parking/id.rs +++ b/library/std/src/sys/sync/thread_parking/id.rs @@ -10,8 +10,8 @@ use crate::cell::UnsafeCell; use crate::pin::Pin; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sync::atomic::{fence, AtomicI8}; -use crate::sys::thread_parking::{current, park, park_timeout, unpark, ThreadId}; +use crate::sync::atomic::{AtomicI8, fence}; +use crate::sys::thread_parking::{ThreadId, current, park, park_timeout, unpark}; use crate::time::Duration; pub struct Parker { diff --git a/library/std/src/sys/sync/thread_parking/windows7.rs b/library/std/src/sys/sync/thread_parking/windows7.rs index 1000b63b6d0..cdd59757fe2 100644 --- a/library/std/src/sys/sync/thread_parking/windows7.rs +++ b/library/std/src/sys/sync/thread_parking/windows7.rs @@ -190,7 +190,7 @@ mod keyed_events { use core::sync::atomic::Ordering::{Acquire, Relaxed}; use core::time::Duration; - use super::{Parker, EMPTY, NOTIFIED}; + use super::{EMPTY, NOTIFIED, Parker}; use crate::sys::c; pub unsafe fn park(parker: Pin<&Parker>) { diff --git a/library/std/src/sys/sync/thread_parking/xous.rs b/library/std/src/sys/sync/thread_parking/xous.rs index 64b6f731f23..28c90249dc2 100644 --- a/library/std/src/sys/sync/thread_parking/xous.rs +++ b/library/std/src/sys/sync/thread_parking/xous.rs @@ -1,5 +1,5 @@ use crate::os::xous::ffi::{blocking_scalar, scalar}; -use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; +use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; use crate::pin::Pin; use crate::ptr; use crate::sync::atomic::AtomicI8; diff --git a/library/std/src/sys/thread_local/guard/key.rs b/library/std/src/sys/thread_local/guard/key.rs index 67c3ca88627..77575547c14 100644 --- a/library/std/src/sys/thread_local/guard/key.rs +++ b/library/std/src/sys/thread_local/guard/key.rs @@ -4,7 +4,7 @@ use crate::ptr; use crate::sys::thread_local::destructors; -use crate::sys::thread_local::key::{set, LazyKey}; +use crate::sys::thread_local::key::{LazyKey, set}; pub fn enable() { static DTORS: LazyKey = LazyKey::new(Some(run)); diff --git a/library/std/src/sys/thread_local/key/tests.rs b/library/std/src/sys/thread_local/key/tests.rs index d82b34e71f0..c7d2c8e6301 100644 --- a/library/std/src/sys/thread_local/key/tests.rs +++ b/library/std/src/sys/thread_local/key/tests.rs @@ -1,4 +1,4 @@ -use super::{get, set, LazyKey}; +use super::{LazyKey, get, set}; use crate::ptr; #[test] diff --git a/library/std/src/sys/thread_local/key/xous.rs b/library/std/src/sys/thread_local/key/xous.rs index 4fb2fdcc619..295fb848b30 100644 --- a/library/std/src/sys/thread_local/key/xous.rs +++ b/library/std/src/sys/thread_local/key/xous.rs @@ -39,7 +39,7 @@ use core::arch::asm; use crate::mem::ManuallyDrop; -use crate::os::xous::ffi::{map_memory, unmap_memory, MemoryFlags}; +use crate::os::xous::ffi::{MemoryFlags, map_memory, unmap_memory}; use crate::ptr; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sync::atomic::{AtomicPtr, AtomicUsize}; diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index e27b47c3f45..f09c396ef0f 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -2,7 +2,7 @@ use super::abort_on_dtor_unwind; use crate::cell::Cell; use crate::marker::PhantomData; use crate::ptr; -use crate::sys::thread_local::key::{get, set, Key, LazyKey}; +use crate::sys::thread_local::key::{Key, LazyKey, get, set}; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 25ebeb3502d..85a82b088cd 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -5,7 +5,7 @@ use crate::ffi::{c_int, c_void}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::sys::common::small_c_string::run_with_cstr; -use crate::sys::net::{cvt, cvt_gai, cvt_r, init, netc as c, wrlen_t, Socket}; +use crate::sys::net::{Socket, cvt, cvt_gai, cvt_r, init, netc as c, wrlen_t}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, fmt, mem, ptr}; diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index 063451ad54e..554e07c1e59 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -18,7 +18,7 @@ #[cfg(test)] mod tests; -use core::char::{encode_utf16_raw, encode_utf8_raw}; +use core::char::{encode_utf8_raw, encode_utf16_raw}; use core::clone::CloneToUninit; use core::str::next_code_point; diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs index b57c99a8452..bc06eaa2b8f 100644 --- a/library/std/src/sys_common/wtf8/tests.rs +++ b/library/std/src/sys_common/wtf8/tests.rs @@ -356,32 +356,32 @@ fn wtf8buf_from_iterator() { fn f(values: &[u32]) -> Wtf8Buf { values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::<Wtf8Buf>() } - assert_eq!( - f(&[0x61, 0xE9, 0x20, 0x1F4A9]), - Wtf8Buf { bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), is_known_utf8: true } - ); + assert_eq!(f(&[0x61, 0xE9, 0x20, 0x1F4A9]), Wtf8Buf { + bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), + is_known_utf8: true + }); assert_eq!(f(&[0xD83D, 0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! - assert_eq!( - f(&[0xD83D, 0x20, 0xDCA9]), - Wtf8Buf { bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), is_known_utf8: false } - ); - assert_eq!( - f(&[0xD800, 0xDBFF]), - Wtf8Buf { bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), is_known_utf8: false } - ); - assert_eq!( - f(&[0xD800, 0xE000]), - Wtf8Buf { bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), is_known_utf8: false } - ); - assert_eq!( - f(&[0xD7FF, 0xDC00]), - Wtf8Buf { bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), is_known_utf8: false } - ); - assert_eq!( - f(&[0x61, 0xDC00]), - Wtf8Buf { bytes: b"\x61\xED\xB0\x80".to_vec(), is_known_utf8: false } - ); + assert_eq!(f(&[0xD83D, 0x20, 0xDCA9]), Wtf8Buf { + bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), + is_known_utf8: false + }); + assert_eq!(f(&[0xD800, 0xDBFF]), Wtf8Buf { + bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), + is_known_utf8: false + }); + assert_eq!(f(&[0xD800, 0xE000]), Wtf8Buf { + bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), + is_known_utf8: false + }); + assert_eq!(f(&[0xD7FF, 0xDC00]), Wtf8Buf { + bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), + is_known_utf8: false + }); + assert_eq!(f(&[0x61, 0xDC00]), Wtf8Buf { + bytes: b"\x61\xED\xB0\x80".to_vec(), + is_known_utf8: false + }); assert_eq!(f(&[0xDC00]), Wtf8Buf { bytes: b"\xED\xB0\x80".to_vec(), is_known_utf8: false }); } @@ -396,36 +396,36 @@ fn wtf8buf_extend() { string } - assert_eq!( - e(&[0x61, 0xE9], &[0x20, 0x1F4A9]), - Wtf8Buf { bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), is_known_utf8: true } - ); + assert_eq!(e(&[0x61, 0xE9], &[0x20, 0x1F4A9]), Wtf8Buf { + bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), + is_known_utf8: true + }); assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! - assert_eq!( - e(&[0xD83D, 0x20], &[0xDCA9]), - Wtf8Buf { bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), is_known_utf8: false } - ); - assert_eq!( - e(&[0xD800], &[0xDBFF]), - Wtf8Buf { bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), is_known_utf8: false } - ); - assert_eq!( - e(&[0xD800], &[0xE000]), - Wtf8Buf { bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), is_known_utf8: false } - ); - assert_eq!( - e(&[0xD7FF], &[0xDC00]), - Wtf8Buf { bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), is_known_utf8: false } - ); - assert_eq!( - e(&[0x61], &[0xDC00]), - Wtf8Buf { bytes: b"\x61\xED\xB0\x80".to_vec(), is_known_utf8: false } - ); - assert_eq!( - e(&[], &[0xDC00]), - Wtf8Buf { bytes: b"\xED\xB0\x80".to_vec(), is_known_utf8: false } - ); + assert_eq!(e(&[0xD83D, 0x20], &[0xDCA9]), Wtf8Buf { + bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), + is_known_utf8: false + }); + assert_eq!(e(&[0xD800], &[0xDBFF]), Wtf8Buf { + bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), + is_known_utf8: false + }); + assert_eq!(e(&[0xD800], &[0xE000]), Wtf8Buf { + bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), + is_known_utf8: false + }); + assert_eq!(e(&[0xD7FF], &[0xDC00]), Wtf8Buf { + bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), + is_known_utf8: false + }); + assert_eq!(e(&[0x61], &[0xDC00]), Wtf8Buf { + bytes: b"\x61\xED\xB0\x80".to_vec(), + is_known_utf8: false + }); + assert_eq!(e(&[], &[0xDC00]), Wtf8Buf { + bytes: b"\xED\xB0\x80".to_vec(), + is_known_utf8: false + }); } #[test] @@ -556,10 +556,9 @@ fn wtf8_encode_wide() { let mut string = Wtf8Buf::from_str("aé "); string.push(CodePoint::from_u32(0xD83D).unwrap()); string.push_char('💩'); - assert_eq!( - string.encode_wide().collect::<Vec<_>>(), - vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9] - ); + assert_eq!(string.encode_wide().collect::<Vec<_>>(), vec![ + 0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9 + ]); } #[test] diff --git a/library/std/src/thread/local/tests.rs b/library/std/src/thread/local/tests.rs index 25019b554bb..6abb9b85a2e 100644 --- a/library/std/src/thread/local/tests.rs +++ b/library/std/src/thread/local/tests.rs @@ -103,6 +103,9 @@ fn smoke_dtor() { #[test] fn circular() { + // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint + #![allow(static_mut_refs)] + struct S1(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>); struct S2(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>); thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None)); diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index e29c28f3c7e..a53e3565dfe 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -162,12 +162,12 @@ use crate::any::Any; use crate::cell::{Cell, OnceCell, UnsafeCell}; use crate::ffi::CStr; use crate::marker::PhantomData; -use crate::mem::{self, forget, ManuallyDrop}; +use crate::mem::{self, ManuallyDrop, forget}; use crate::num::NonZero; use crate::pin::Pin; use crate::ptr::addr_of_mut; -use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sync::Arc; +use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sys::sync::Parker; use crate::sys::thread as imp; use crate::sys_common::{AsInner, IntoInner}; @@ -178,7 +178,7 @@ use crate::{env, fmt, io, panic, panicking, str}; mod scoped; #[stable(feature = "scoped_threads", since = "1.63.0")] -pub use scoped::{scope, Scope, ScopedJoinHandle}; +pub use scoped::{Scope, ScopedJoinHandle, scope}; //////////////////////////////////////////////////////////////////////////////// // Thread-local storage @@ -432,7 +432,7 @@ impl Builder { /// ``` /// /// [`io::Result`]: crate::io::Result - #[stable(feature = "thread_spawn_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "thread_spawn_unchecked", since = "1.82.0")] pub unsafe fn spawn_unchecked<F, T>(self, f: F) -> io::Result<JoinHandle<T>> where F: FnOnce() -> T, @@ -1503,6 +1503,54 @@ impl Thread { self.inner.name.as_str() } + /// Consumes the `Thread`, returning a raw pointer. + /// + /// To avoid a memory leak the pointer must be converted + /// back into a `Thread` using [`Thread::from_raw`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(thread_raw)] + /// + /// use std::thread::{self, Thread}; + /// + /// let thread = thread::current(); + /// let id = thread.id(); + /// let ptr = Thread::into_raw(thread); + /// unsafe { + /// assert_eq!(Thread::from_raw(ptr).id(), id); + /// } + /// ``` + #[unstable(feature = "thread_raw", issue = "97523")] + pub fn into_raw(self) -> *const () { + // Safety: We only expose an opaque pointer, which maintains the `Pin` invariant. + let inner = unsafe { Pin::into_inner_unchecked(self.inner) }; + Arc::into_raw(inner) as *const () + } + + /// Constructs a `Thread` from a raw pointer. + /// + /// The raw pointer must have been previously returned + /// by a call to [`Thread::into_raw`]. + /// + /// # Safety + /// + /// This function is unsafe because improper use may lead + /// to memory unsafety, even if the returned `Thread` is never + /// accessed. + /// + /// Creating a `Thread` from a pointer other than one returned + /// from [`Thread::into_raw`] is **undefined behavior**. + /// + /// Calling this function twice on the same raw pointer can lead + /// to a double-free if both `Thread` instances are dropped. + #[unstable(feature = "thread_raw", issue = "97523")] + pub unsafe fn from_raw(ptr: *const ()) -> Thread { + // Safety: Upheld by caller. + unsafe { Thread { inner: Pin::new_unchecked(Arc::from_raw(ptr as *const Inner)) } } + } + fn cname(&self) -> Option<&CStr> { self.inner.name.as_cstr() } diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index ba27c9220ae..b2305b1eda7 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -1,8 +1,8 @@ -use super::{current, park, Builder, JoinInner, Result, Thread}; +use super::{Builder, JoinInner, Result, Thread, current, park}; use crate::marker::PhantomData; -use crate::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; -use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind}; use crate::sync::Arc; +use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use crate::{fmt, io}; /// A scope to spawn scoped threads in. diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index aa464d56f95..ff45e82bd9c 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -2,7 +2,7 @@ use super::Builder; use crate::any::Any; use crate::panic::panic_any; use crate::sync::atomic::{AtomicBool, Ordering}; -use crate::sync::mpsc::{channel, Sender}; +use crate::sync::mpsc::{Sender, channel}; use crate::sync::{Arc, Barrier}; use crate::thread::{self, Scope, ThreadId}; use crate::time::{Duration, Instant}; diff --git a/library/std/src/time.rs b/library/std/src/time.rs index ae46670c25e..f28a0568a3c 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -280,6 +280,7 @@ impl Instant { /// ``` #[must_use] #[stable(feature = "time2", since = "1.8.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "instant_now")] pub fn now() -> Instant { Instant(time::Instant::now()) } diff --git a/library/std/src/time/tests.rs b/library/std/src/time/tests.rs index de36dc4c9fd..e88f2d5e806 100644 --- a/library/std/src/time/tests.rs +++ b/library/std/src/time/tests.rs @@ -1,7 +1,7 @@ use core::fmt::Debug; #[cfg(not(target_arch = "wasm32"))] -use test::{black_box, Bencher}; +use test::{Bencher, black_box}; use super::{Duration, Instant, SystemTime, UNIX_EPOCH}; @@ -235,8 +235,8 @@ macro_rules! bench_instant_threaded { #[bench] #[cfg(not(target_arch = "wasm32"))] fn $bench_name(b: &mut Bencher) -> crate::thread::Result<()> { - use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::Arc; + use crate::sync::atomic::{AtomicBool, Ordering}; let running = Arc::new(AtomicBool::new(true)); |
