diff options
Diffstat (limited to 'library/std/src')
79 files changed, 1835 insertions, 948 deletions
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 5d0de2d06a0..b1102b440e0 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -37,7 +37,7 @@ use crate::time::SystemTime; /// /// # Examples /// -/// Creates a new file and write bytes to it (you can also use [`write()`]): +/// Creates a new file and write bytes to it (you can also use [`write`]): /// /// ```no_run /// use std::fs::File; @@ -97,7 +97,7 @@ use crate::time::SystemTime; /// have been opened for asynchronous I/O (e.g. by using `FILE_FLAG_OVERLAPPED`). /// /// [`BufReader`]: io::BufReader -/// [`BufWriter`]: io::BufReader +/// [`BufWriter`]: io::BufWriter /// [`sync_all`]: File::sync_all /// [`write`]: File::write /// [`read`]: File::read @@ -385,7 +385,7 @@ impl File { /// See the [`OpenOptions::open`] function for more details. /// /// See also [`std::fs::write()`][self::write] for a simple function to - /// create a file with a given data. + /// create a file with some given data. /// /// # Examples /// @@ -1036,7 +1036,7 @@ impl OpenOptions { /// [`OpenOptions::append`] access must be used. /// /// See also [`std::fs::write()`][self::write] for a simple function to - /// create a file with a given data. + /// create a file with some given data. /// /// # Examples /// @@ -2018,7 +2018,7 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> /// the length of the `to` file as reported by `metadata`. /// /// If you want to copy the contents of one file to another and you’re -/// working with [`File`]s, see the [`io::copy()`] function. +/// working with [`File`]s, see the [`io::copy`](io::copy()) function. /// /// # Platform-specific behavior /// diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 65dec3863cc..6a92832fcdb 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1644,8 +1644,8 @@ fn test_file_times() { use crate::os::macos::fs::FileTimesExt; #[cfg(target_os = "tvos")] use crate::os::tvos::fs::FileTimesExt; - #[cfg(target_os = "tvos")] - use crate::os::tvos::fs::FileTimesExt; + #[cfg(target_os = "visionos")] + use crate::os::visionos::fs::FileTimesExt; #[cfg(target_os = "watchos")] use crate::os::watchos::fs::FileTimesExt; #[cfg(windows)] @@ -1662,6 +1662,7 @@ fn test_file_times() { target_os = "macos", target_os = "ios", target_os = "watchos", + target_os = "visionos", target_os = "tvos", ))] let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); @@ -1670,6 +1671,7 @@ fn test_file_times() { target_os = "macos", target_os = "ios", target_os = "watchos", + target_os = "visionos", target_os = "tvos", ))] { @@ -1701,6 +1703,7 @@ fn test_file_times() { target_os = "macos", target_os = "ios", target_os = "watchos", + target_os = "visionos", target_os = "tvos", ))] { @@ -1709,7 +1712,13 @@ fn test_file_times() { } #[test] -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "visionos" +))] fn test_file_times_pre_epoch_with_nanos() { #[cfg(target_os = "ios")] use crate::os::ios::fs::FileTimesExt; @@ -1717,6 +1726,8 @@ fn test_file_times_pre_epoch_with_nanos() { use crate::os::macos::fs::FileTimesExt; #[cfg(target_os = "tvos")] use crate::os::tvos::fs::FileTimesExt; + #[cfg(target_os = "visionos")] + use crate::os::visionos::fs::FileTimesExt; #[cfg(target_os = "watchos")] use crate::os::watchos::fs::FileTimesExt; diff --git a/library/std/src/lib.miri.rs b/library/std/src/lib.miri.rs new file mode 100644 index 00000000000..1f9bfb5b1b5 --- /dev/null +++ b/library/std/src/lib.miri.rs @@ -0,0 +1,4 @@ +//! Grep bootstrap for `MIRI_REPLACE_LIBRS_IF_NOT_TEST` to learn what this is about. +#![no_std] +extern crate std as realstd; +pub use realstd::*; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index c457c39e0c1..c713eefc72c 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -212,15 +212,10 @@ //! [rust-discord]: https://discord.gg/rust-lang //! [array]: prim@array //! [slice]: prim@slice -// To run std tests without x.py without ending up with two copies of std, Miri needs to be -// able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>. -// rustc itself never sets the feature, so this line has no effect there. -#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))] -// miri-test-libstd also prefers to make std use the sysroot versions of the dependencies. -#![cfg_attr(feature = "miri-test-libstd", feature(rustc_private))] -// + #![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))] #![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))] +#![cfg_attr(not(bootstrap), rustc_preserve_ub_checks)] #![doc( html_playground_url = "https://play.rust-lang.org/", issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", @@ -328,6 +323,7 @@ #![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(float_next_up_down)] +#![feature(fmt_internals)] #![feature(generic_nonzero)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] @@ -339,7 +335,7 @@ #![feature(panic_can_unwind)] #![feature(panic_info_message)] #![feature(panic_internals)] -#![feature(pointer_is_aligned)] +#![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(prelude_2024)] #![feature(ptr_as_uninit)] @@ -580,6 +576,9 @@ pub mod net; pub mod num; pub mod os; pub mod panic; +#[cfg(not(bootstrap))] +#[unstable(feature = "core_pattern_types", issue = "none")] +pub mod pat; pub mod path; pub mod process; pub mod sync; diff --git a/library/std/src/os/freebsd/net.rs b/library/std/src/os/freebsd/net.rs index 33990d54caa..b7e0fdc0a9a 100644 --- a/library/std/src/os/freebsd/net.rs +++ b/library/std/src/os/freebsd/net.rs @@ -2,6 +2,7 @@ #![unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +use crate::ffi::CStr; use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; @@ -40,6 +41,15 @@ pub trait UnixSocketExt: Sealed { /// ``` #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()>; + + /// Get a filter name if one had been set previously on the socket. + #[unstable(feature = "acceptfilter", issue = "121891")] + fn acceptfilter(&self) -> io::Result<&CStr>; + + /// Set or disable a filter on the socket to filter incoming connections + /// to defer it before accept(2) + #[unstable(feature = "acceptfilter", issue = "121891")] + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()>; } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -51,6 +61,14 @@ impl UnixSocketExt for net::UnixDatagram { fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { self.as_inner().set_local_creds_persistent(local_creds_persistent) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -62,4 +80,12 @@ impl UnixSocketExt for net::UnixStream { fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { self.as_inner().set_local_creds_persistent(local_creds_persistent) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index bebf3a797d8..ca3584e82f9 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -149,6 +149,8 @@ pub mod solid; pub(crate) mod tvos; #[cfg(target_os = "uefi")] pub mod uefi; +#[cfg(target_os = "visionos")] +pub(crate) mod visionos; #[cfg(target_os = "vita")] pub mod vita; #[cfg(target_os = "vxworks")] diff --git a/library/std/src/os/netbsd/net.rs b/library/std/src/os/netbsd/net.rs index 5c82f43077d..b9679c7b3af 100644 --- a/library/std/src/os/netbsd/net.rs +++ b/library/std/src/os/netbsd/net.rs @@ -2,6 +2,7 @@ #![unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +use crate::ffi::CStr; use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; @@ -40,6 +41,15 @@ pub trait UnixSocketExt: Sealed { /// ``` #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] fn set_local_creds(&self, local_creds: bool) -> io::Result<()>; + + /// Get a filter name if one had been set previously on the socket. + #[unstable(feature = "acceptfilter", issue = "121891")] + fn acceptfilter(&self) -> io::Result<&CStr>; + + /// Set or disable a filter on the socket to filter incoming connections + /// to defer it before accept(2) + #[unstable(feature = "acceptfilter", issue = "121891")] + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()>; } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -51,6 +61,14 @@ impl UnixSocketExt for net::UnixDatagram { fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { self.as_inner().set_local_creds(local_creds) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -62,4 +80,12 @@ impl UnixSocketExt for net::UnixStream { fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { self.as_inner().set_local_creds(local_creds) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index b0633fd7bfc..d7a622012a5 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -79,6 +79,8 @@ mod platform { pub use crate::os::solaris::*; #[cfg(target_os = "tvos")] pub use crate::os::tvos::*; + #[cfg(target_os = "visionos")] + pub use crate::os::visionos::*; #[cfg(target_os = "vita")] pub use crate::os::vita::*; #[cfg(target_os = "vxworks")] diff --git a/library/std/src/os/unix/net/mod.rs b/library/std/src/os/unix/net/mod.rs index 28c1188677b..16d9cd915ac 100644 --- a/library/std/src/os/unix/net/mod.rs +++ b/library/std/src/os/unix/net/mod.rs @@ -20,6 +20,7 @@ mod tests; target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "macos", target_os = "netbsd", target_os = "openbsd", @@ -46,6 +47,7 @@ pub use self::stream::*; target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "macos", target_os = "netbsd", target_os = "openbsd", diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index d67493aaf4d..82b24dca1c4 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -7,6 +7,7 @@ target_os = "tvos", target_os = "macos", target_os = "watchos", + target_os = "visionos", target_os = "netbsd", target_os = "openbsd", target_os = "nto" @@ -234,6 +235,7 @@ impl UnixStream { target_os = "tvos", target_os = "macos", target_os = "watchos", + target_os = "visionos", target_os = "netbsd", target_os = "openbsd", target_os = "nto" @@ -580,6 +582,10 @@ impl io::Read for UnixStream { io::Read::read(&mut &*self, buf) } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + io::Read::read_buf(&mut &*self, buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { io::Read::read_vectored(&mut &*self, bufs) } @@ -596,6 +602,10 @@ impl<'a> io::Read for &'a UnixStream { self.0.read(buf) } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { self.0.read_vectored(bufs) } diff --git a/library/std/src/os/unix/net/ucred.rs b/library/std/src/os/unix/net/ucred.rs index 4c915c57906..3a752a53a50 100644 --- a/library/std/src/os/unix/net/ucred.rs +++ b/library/std/src/os/unix/net/ucred.rs @@ -35,7 +35,13 @@ pub(super) use self::impl_linux::peer_cred; ))] pub(super) use self::impl_bsd::peer_cred; -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "visionos" +))] pub(super) use self::impl_mac::peer_cred; #[cfg(any(target_os = "linux", target_os = "android"))] @@ -97,7 +103,13 @@ mod impl_bsd { } } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "visionos" +))] mod impl_mac { use super::UCred; use crate::os::unix::io::AsRawFd; diff --git a/library/std/src/os/unix/net/ucred/tests.rs b/library/std/src/os/unix/net/ucred/tests.rs index dd99ecdd819..2a0797877c6 100644 --- a/library/std/src/os/unix/net/ucred/tests.rs +++ b/library/std/src/os/unix/net/ucred/tests.rs @@ -11,6 +11,7 @@ use libc::{getegid, geteuid, getpid}; target_os = "tvos", target_os = "macos", target_os = "watchos", + target_os = "visionos", target_os = "openbsd" ))] fn test_socket_pair() { @@ -32,6 +33,7 @@ fn test_socket_pair() { target_os = "ios", target_os = "macos", target_os = "watchos", + target_os = "visionos", target_os = "tvos", ))] fn test_socket_pair_pids(arg: Type) -> RetType { diff --git a/library/std/src/os/visionos/fs.rs b/library/std/src/os/visionos/fs.rs new file mode 100644 index 00000000000..e5df4de0b7f --- /dev/null +++ b/library/std/src/os/visionos/fs.rs @@ -0,0 +1,160 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::{self, Metadata}; +use crate::sealed::Sealed; +use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; +use crate::time::SystemTime; + +#[allow(deprecated)] +use super::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[deprecated( + since = "1.8.0", + note = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_flags(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gen(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_lspare(&self) -> u32; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + 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 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_birthtime(&self) -> i64 { + self.as_inner().as_inner().st_birthtime as i64 + } + fn st_birthtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_birthtime_nsec as i64 + } + 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 + } + fn st_gen(&self) -> u32 { + self.as_inner().as_inner().st_gen as u32 + } + fn st_flags(&self) -> u32 { + self.as_inner().as_inner().st_flags as u32 + } + fn st_lspare(&self) -> u32 { + self.as_inner().as_inner().st_lspare as u32 + } +} + +/// OS-specific extensions to [`fs::FileTimes`]. +#[stable(feature = "file_set_times", since = "1.75.0")] +pub trait FileTimesExt: Sealed { + /// Set the creation time of a file. + #[stable(feature = "file_set_times", since = "1.75.0")] + fn set_created(self, t: SystemTime) -> Self; +} + +#[stable(feature = "file_set_times", since = "1.75.0")] +impl FileTimesExt for fs::FileTimes { + fn set_created(mut self, t: SystemTime) -> Self { + self.as_inner_mut().set_created(t.into_inner()); + self + } +} diff --git a/library/std/src/os/visionos/mod.rs b/library/std/src/os/visionos/mod.rs new file mode 100644 index 00000000000..f4b061ffda8 --- /dev/null +++ b/library/std/src/os/visionos/mod.rs @@ -0,0 +1,6 @@ +//! visionos-specific definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod fs; +pub mod raw; diff --git a/library/std/src/os/visionos/raw.rs b/library/std/src/os/visionos/raw.rs new file mode 100644 index 00000000000..2b3eca6f493 --- /dev/null +++ b/library/std/src/os/visionos/raw.rs @@ -0,0 +1,83 @@ +//! visionos-specific 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)] + +use crate::os::raw::c_long; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blkcnt_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blksize_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type ino_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type nlink_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type off_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type time_t = i64; + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = usize; + +#[repr(C)] +#[derive(Clone)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u16, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u16, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_birthtime: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_birthtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_flags: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gen: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_lspare: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_qspare: [i64; 2], +} diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 1be3acf5d43..15ab2250122 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -199,8 +199,60 @@ pub trait CommandExt: Sealed { /// Append literal text to the command line without any quoting or escaping. /// - /// This is useful for passing arguments to `cmd.exe /c`, which doesn't follow - /// `CommandLineToArgvW` escaping rules. + /// This is useful for passing arguments to applications which doesn't follow + /// the standard C run-time escaping rules, such as `cmd.exe /c`. + /// + /// # Bat files + /// + /// Note the `cmd /c` command line has slightly different escaping rules then bat files + /// themselves. If possible, it may be better to write complex arguments to a temporary + /// .bat file, with appropriate escaping, and simply run that using: + /// + /// ```no_run + /// # use std::process::Command; + /// # let temp_bat_file = ""; + /// # #[allow(unused)] + /// let output = Command::new("cmd").args(["/c", &format!("\"{temp_bat_file}\"")]).output(); + /// ``` + /// + /// # Example + /// + /// Run a bat script using both trusted and untrusted arguments. + /// + /// ```no_run + /// #[cfg(windows)] + /// // `my_script_path` is a path to known bat file. + /// // `user_name` is an untrusted name given by the user. + /// fn run_script( + /// my_script_path: &str, + /// user_name: &str, + /// ) -> Result<std::process::Output, std::io::Error> { + /// use std::io::{Error, ErrorKind}; + /// use std::os::windows::process::CommandExt; + /// use std::process::Command; + /// + /// // Create the command line, making sure to quote the script path. + /// // This assumes the fixed arguments have been tested to work with the script we're using. + /// let mut cmd_args = format!(r#""{my_script_path}" "--features=[a,b,c]""#); + /// + /// // Make sure the user name is safe. In particular we need to be + /// // cautious of ascii symbols that cmd may interpret specially. + /// // Here we only allow alphanumeric characters. + /// if !user_name.chars().all(|c| c.is_alphanumeric()) { + /// return Err(Error::new(ErrorKind::InvalidInput, "invalid user name")); + /// } + /// // now we've checked the user name, let's add that too. + /// cmd_args.push(' '); + /// cmd_args.push_str(&format!("--user {user_name}")); + /// + /// // call cmd.exe and return the output + /// Command::new("cmd.exe") + /// .arg("/c") + /// // surround the entire command in an extra pair of quotes, as required by cmd.exe. + /// .raw_arg(&format!("\"{cmd_args}\"")) + /// .output() + /// } + /// ```` #[stable(feature = "windows_process_extensions_raw_arg", since = "1.62.0")] fn raw_arg<S: AsRef<OsStr>>(&mut self, text_to_append_as_is: S) -> &mut process::Command; diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs index 7fe84db515c..e9a9f533720 100644 --- a/library/std/src/os/xous/ffi.rs +++ b/library/std/src/os/xous/ffi.rs @@ -389,7 +389,7 @@ pub(crate) unsafe fn map_memory<T>( let result = a0; if result == SyscallResult::MemoryRange as usize { - let start = core::ptr::from_exposed_addr_mut::<T>(a1); + let start = core::ptr::with_exposed_provenance_mut::<T>(a1); let len = a2 / core::mem::size_of::<T>(); let end = unsafe { start.add(len) }; Ok(unsafe { core::slice::from_raw_parts_mut(start, len) }) diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 3d576af681e..e63b46ab705 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -126,6 +126,9 @@ where /// Also note that unwinding into Rust code with a foreign exception (e.g. /// an exception thrown from C++ code) is undefined behavior. /// +/// Finally, be **careful in how you drop the result of this function**. +/// If it is `Err`, it contains the panic payload, and dropping that may in turn panic! +/// /// # Examples /// /// ``` diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index b0bcab7994c..f46e1e171d2 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -21,7 +21,6 @@ use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{PoisonError, RwLock}; use crate::sys::stdio::panic_output; use crate::sys_common::backtrace; -use crate::sys_common::thread_info; use crate::thread; #[cfg(not(test))] @@ -256,7 +255,7 @@ fn default_hook(info: &PanicInfo<'_>) { None => "Box<dyn Any>", }, }; - let thread = thread_info::current_thread(); + 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| { @@ -391,6 +390,7 @@ pub mod panic_count { pub fn increase(run_panic_hook: bool) -> Option<MustAbort> { let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed); if global_count & ALWAYS_ABORT_FLAG != 0 { + // Do *not* access thread-local state, we might be after a `fork`. return Some(MustAbort::AlwaysAbort); } @@ -744,13 +744,22 @@ fn rust_panic_with_hook( if let Some(must_abort) = must_abort { match must_abort { panic_count::MustAbort::PanicInHook => { - // Don't try to print the message in this case - // - perhaps that is causing the recursive panics. - rtprintpanic!("thread panicked while processing panic. aborting.\n"); + // Don't try to format the message in this case, perhaps that is causing the + // recursive panics. However if the message is just a string, no user-defined + // code is involved in printing it, so that is risk-free. + let msg_str = message.and_then(|m| m.as_str()).map(|m| [m]); + let message = msg_str.as_ref().map(|m| fmt::Arguments::new_const(m)); + let panicinfo = PanicInfo::internal_constructor( + message.as_ref(), + location, + can_unwind, + force_no_backtrace, + ); + rtprintpanic!("{panicinfo}\nthread panicked while processing panic. aborting.\n"); } panic_count::MustAbort::AlwaysAbort => { // Unfortunately, this does not print a backtrace, because creating - // a `Backtrace` will allocate, which we must to avoid here. + // a `Backtrace` will allocate, which we must avoid here. let panicinfo = PanicInfo::internal_constructor( message, location, diff --git a/library/std/src/pat.rs b/library/std/src/pat.rs new file mode 100644 index 00000000000..aeddd84c2cb --- /dev/null +++ b/library/std/src/pat.rs @@ -0,0 +1,3 @@ +//! Helper module for exporting the `pattern_type` macro + +pub use core::pattern_type; diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/common.rs index 7a7a7737635..f61e04e02b6 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/common.rs @@ -1,9 +1,7 @@ -//! The first version of the prelude of The Rust Standard Library. +//! Items common to the prelude of all editions. //! //! See the [module-level documentation](super) for more. -#![stable(feature = "rust1", since = "1.0.0")] - // Re-exported core operators #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] @@ -52,11 +50,6 @@ pub use core::prelude::v1::{ #[doc(no_inline)] pub use core::prelude::v1::concat_bytes; -// Do not `doc(inline)` these `doc(hidden)` items. -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow(deprecated)] -pub use core::prelude::v1::{RustcDecodable, RustcEncodable}; - // Do not `doc(no_inline)` so that they become doc items on their own // (no public module for them to be re-exported from). #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] @@ -91,6 +84,15 @@ pub use core::prelude::v1::cfg_eval; )] pub use core::prelude::v1::type_ascribe; +#[cfg(not(bootstrap))] +// Do not `doc(no_inline)` either. +#[unstable( + feature = "deref_patterns", + issue = "87121", + reason = "placeholder syntax for deref patterns" +)] +pub use core::prelude::v1::deref; + // The file so far is equivalent to core/src/prelude/v1.rs. It is duplicated // rather than glob imported because we want docs to show these re-exports as // pointing to within `std`. diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 7d44d2e4b5d..0bdbab716ad 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -93,7 +93,26 @@ #![stable(feature = "rust1", since = "1.0.0")] -pub mod v1; +mod common; + +/// The first version of the prelude of The Rust Standard Library. +/// +/// See the [module-level documentation](self) for more. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod v1 { + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::common::*; + + // Do not `doc(inline)` these `doc(hidden)` items. + #[unstable( + feature = "rustc_encodable_decodable", + issue = "none", + soft, + reason = "derive macro for `rustc-serialize`; should not be used in new code" + )] + #[allow(deprecated)] + pub use core::prelude::v1::{RustcDecodable, RustcEncodable}; +} /// The 2015 version of the prelude of The Rust Standard Library. /// @@ -134,9 +153,8 @@ pub mod rust_2021 { /// See the [module-level documentation](self) for more. #[unstable(feature = "prelude_2024", issue = "121042")] pub mod rust_2024 { - #[unstable(feature = "prelude_2024", issue = "121042")] - #[doc(no_inline)] - pub use super::v1::*; + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::common::*; #[unstable(feature = "prelude_2024", issue = "121042")] #[doc(no_inline)] diff --git a/library/std/src/process.rs b/library/std/src/process.rs index b84d5f11954..69cc61b30ef 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -88,6 +88,47 @@ //! assert_eq!(b"test", output.stdout.as_slice()); //! ``` //! +//! # Windows argument splitting +//! +//! On Unix systems arguments are passed to a new process as an array of strings +//! but on Windows arguments are passed as a single commandline string and it's +//! up to the child process to parse it into an array. Therefore the parent and +//! child processes must agree on how the commandline string is encoded. +//! +//! Most programs use the standard C run-time `argv`, which in practice results +//! in consistent argument handling. However some programs have their own way of +//! parsing the commandline string. In these cases using [`arg`] or [`args`] may +//! result in the child process seeing a different array of arguments then the +//! parent process intended. +//! +//! Two ways of mitigating this are: +//! +//! * Validate untrusted input so that only a safe subset is allowed. +//! * Use [`raw_arg`] to build a custom commandline. This bypasses the escaping +//! rules used by [`arg`] so should be used with due caution. +//! +//! `cmd.exe` and `.bat` use non-standard argument parsing and are especially +//! vulnerable to malicious input as they may be used to run arbitrary shell +//! commands. Untrusted arguments should be restricted as much as possible. +//! For examples on handling this see [`raw_arg`]. +//! +//! ### Bat file special handling +//! +//! On Windows, `Command` uses the Windows API function [`CreateProcessW`] to +//! spawn new processes. An undocumented feature of this function is that, +//! when given a `.bat` file as the application to run, it will automatically +//! convert that into running `cmd.exe /c` with the bat file as the next argument. +//! +//! For historical reasons Rust currently preserves this behaviour when using +//! [`Command::new`], and escapes the arguments according to `cmd.exe` rules. +//! Due to the complexity of `cmd.exe` argument handling, it might not be +//! possible to safely escape some special chars, and using them will result +//! in an error being returned at process spawn. The set of unescapeable +//! special chars might change between releases. +//! +//! Also note that running `.bat` scripts in this way may be removed in the +//! future and so should not be relied upon. +//! //! [`spawn`]: Command::spawn //! [`output`]: Command::output //! @@ -97,6 +138,12 @@ //! //! [`Write`]: io::Write //! [`Read`]: io::Read +//! +//! [`arg`]: Command::arg +//! [`args`]: Command::args +//! [`raw_arg`]: crate::os::windows::process::CommandExt::raw_arg +//! +//! [`CreateProcessW`]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw #![stable(feature = "process", since = "1.0.0")] #![deny(unsafe_op_in_unsafe_fn)] @@ -611,6 +658,22 @@ impl Command { /// escaped characters, word splitting, glob patterns, variable substitution, etc. /// have no effect. /// + /// <div class="warning"> + /// + /// On Windows use caution with untrusted inputs. Most applications use the + /// standard convention for decoding arguments passed to them. These are safe to use with `arg`. + /// However some applications, such as `cmd.exe` and `.bat` files, use a non-standard way of decoding arguments + /// and are therefore vulnerable to malicious input. + /// In the case of `cmd.exe` this is especially important because a malicious argument can potentially run arbitrary shell commands. + /// + /// See [Windows argument splitting][windows-args] for more details + /// or [`raw_arg`] for manually implementing non-standard argument encoding. + /// + /// [`raw_arg`]: crate::os::windows::process::CommandExt::raw_arg + /// [windows-args]: crate::process#windows-argument-splitting + /// + /// </div> + /// /// # Examples /// /// Basic usage: @@ -641,6 +704,22 @@ impl Command { /// escaped characters, word splitting, glob patterns, variable substitution, etc. /// have no effect. /// + /// <div class="warning"> + /// + /// On Windows use caution with untrusted inputs. Most applications use the + /// standard convention for decoding arguments passed to them. These are safe to use with `args`. + /// However some applications, such as `cmd.exe` and `.bat` files, use a non-standard way of decoding arguments + /// and are therefore vulnerable to malicious input. + /// In the case of `cmd.exe` this is especially important because a malicious argument can potentially run arbitrary shell commands. + /// + /// See [Windows argument splitting][windows-args] for more details + /// or [`raw_arg`] for manually implementing non-standard argument encoding. + /// + /// [`raw_arg`]: crate::os::windows::process::CommandExt::raw_arg + /// [windows-args]: crate::process#windows-argument-splitting + /// + /// </div> + /// /// # Examples /// /// Basic usage: diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 335944845ae..59e118f81ab 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -16,16 +16,13 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(unused_macros)] -use crate::ffi::CString; - // Re-export some of our utilities which are expected by other crates. pub use crate::panicking::{begin_panic, panic_count}; pub use core::panicking::{panic_display, panic_fmt}; use crate::sync::Once; use crate::sys; -use crate::sys_common::thread_info; -use crate::thread::Thread; +use crate::thread::{self, Thread}; // Prints to the "panic output", depending on the platform this may be: // - the standard error output @@ -96,13 +93,9 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { unsafe { sys::init(argc, argv, sigpipe); - let main_guard = sys::thread::guard::init(); - // Next, set up the current Thread with the guard information we just - // created. Note that this isn't necessary in general for new threads, - // but we just do this to name the main thread and to give it correct - // info about the stack bounds. - let thread = Thread::new(Some(rtunwrap!(Ok, CString::new("main")))); - thread_info::set(main_guard, thread); + // Set up the current thread to give it the right name. + let thread = Thread::new_main(); + thread::set_current(thread); } } diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index 895fcbd6b7e..d417034f5af 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -396,7 +396,7 @@ impl<T: ?Sized> Mutex<T> { self.poison.get() } - /// Clear the poisoned state from a mutex + /// Clear the poisoned state from a mutex. /// /// If the mutex is poisoned, it will remain poisoned until this function is called. This /// allows recovering from a poisoned state and marking that it has recovered. For example, if diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 6d068613f8f..fc830bacced 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -252,6 +252,46 @@ impl<T> OnceLock<T> { } } + /// Gets the mutable reference of the contents of the cell, initializing + /// it with `f` if the cell was empty. + /// + /// Many threads may call `get_mut_or_init` concurrently with different + /// initializing functions, but it is guaranteed that only one function + /// will be executed. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell_get_mut)] + /// + /// use std::sync::OnceLock; + /// + /// let mut cell = OnceLock::new(); + /// let value = cell.get_mut_or_init(|| 92); + /// assert_eq!(*value, 92); + /// + /// *value += 2; + /// assert_eq!(*value, 94); + /// + /// let value = cell.get_mut_or_init(|| unreachable!()); + /// assert_eq!(*value, 94); + /// ``` + #[inline] + #[unstable(feature = "once_cell_get_mut", issue = "121641")] + pub fn get_mut_or_init<F>(&mut self, f: F) -> &mut T + where + F: FnOnce() -> T, + { + match self.get_mut_or_try_init(|| Ok::<T, !>(f())) { + Ok(val) => val, + } + } + /// Gets the contents of the cell, initializing it with `f` if /// the cell was empty. If the cell was empty and `f` failed, an /// error is returned. @@ -303,6 +343,47 @@ impl<T> OnceLock<T> { Ok(unsafe { self.get_unchecked() }) } + /// Gets the mutable reference of the contents of the cell, initializing + /// it with `f` if the cell was empty. If the cell was empty and `f` failed, + /// an error is returned. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and + /// the cell remains uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell_get_mut)] + /// + /// use std::sync::OnceLock; + /// + /// let mut cell: OnceLock<u32> = OnceLock::new(); + /// + /// // Failed initializers do not change the value + /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); + /// assert!(cell.get().is_none()); + /// + /// let value = cell.get_mut_or_try_init(|| "1234".parse()); + /// assert_eq!(value, Ok(&mut 1234)); + /// *value.unwrap() += 2; + /// assert_eq!(cell.get(), Some(&1236)) + /// ``` + #[inline] + #[unstable(feature = "once_cell_get_mut", issue = "121641")] + pub fn get_mut_or_try_init<F, E>(&mut self, f: F) -> Result<&mut T, E> + where + F: FnOnce() -> Result<T, E>, + { + if self.get().is_none() { + self.initialize(f)?; + } + debug_assert!(self.is_initialized()); + // SAFETY: The inner value has been initialized + Ok(unsafe { self.get_unchecked_mut() }) + } + /// Consumes the `OnceLock`, returning the wrapped value. Returns /// `None` if the cell was empty. /// diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index f7f098c082a..e0a8a7603d7 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -31,13 +31,14 @@ use crate::sys::sync as sys; /// <details><summary>Potential deadlock example</summary> /// /// ```text -/// // Thread 1 | // Thread 2 -/// let _rg = lock.read(); | -/// | // will block -/// | let _wg = lock.write(); -/// // may deadlock | -/// let _rg = lock.read(); | +/// // Thread 1 | // Thread 2 +/// let _rg1 = lock.read(); | +/// | // will block +/// | let _wg = lock.write(); +/// // may deadlock | +/// let _rg2 = lock.read(); | /// ``` +/// /// </details> /// /// The type parameter `T` represents the data that this lock protects. It is @@ -439,7 +440,7 @@ impl<T: ?Sized> RwLock<T> { self.poison.get() } - /// Clear the poisoned state from a lock + /// Clear the poisoned state from a lock. /// /// If the lock is poisoned, it will remain poisoned until this function is called. This allows /// recovering from a poisoned state and marking that it has recovered. For example, if the diff --git a/library/std/src/sys/pal/hermit/fd.rs b/library/std/src/sys/pal/hermit/fd.rs index 5eb828fea1f..962577bb1ed 100644 --- a/library/std/src/sys/pal/hermit/fd.rs +++ b/library/std/src/sys/pal/hermit/fd.rs @@ -48,6 +48,11 @@ impl FileDesc { pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> { unsupported() } + + pub fn fstat(&self, stat: *mut abi::stat) -> io::Result<()> { + cvt(unsafe { abi::fstat(self.fd.as_raw_fd(), stat) })?; + Ok(()) + } } impl<'a> Read for &'a FileDesc { diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index d4da53fd3df..6519cc22f1f 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -1,12 +1,17 @@ -use super::abi::{self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY}; +use super::abi::{ + self, dirent64, stat as stat_struct, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, + O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, +}; use super::fd::FileDesc; -use crate::ffi::{CStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; -use crate::hash::{Hash, Hasher}; use crate::io::{self, Error, ErrorKind}; use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem; +use crate::os::hermit::ffi::OsStringExt; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; +use crate::sync::Arc; use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::cvt; use crate::sys::time::SystemTime; @@ -14,16 +19,53 @@ use crate::sys::unsupported; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; pub use crate::sys_common::fs::{copy, try_exists}; -//pub use crate::sys_common::fs::remove_dir_all; #[derive(Debug)] pub struct File(FileDesc); +#[derive(Clone)] +pub struct FileAttr { + stat_val: stat_struct, +} -pub struct FileAttr(!); +impl FileAttr { + fn from_stat(stat_val: stat_struct) -> Self { + Self { stat_val } + } +} -pub struct ReadDir(!); +// all DirEntry's will have a reference to this struct +struct InnerReadDir { + root: PathBuf, + dir: Vec<u8>, +} -pub struct DirEntry(!); +impl InnerReadDir { + pub fn new(root: PathBuf, dir: Vec<u8>) -> Self { + Self { root, dir } + } +} + +pub struct ReadDir { + inner: Arc<InnerReadDir>, + pos: i64, +} + +impl ReadDir { + fn new(inner: InnerReadDir) -> Self { + Self { inner: Arc::new(inner), pos: 0 } + } +} + +pub struct DirEntry { + /// path to the entry + root: PathBuf, + /// 64-bit inode number + ino: u64, + /// File type + type_: u32, + /// name of the entry + name: OsString, +} #[derive(Clone, Debug)] pub struct OpenOptions { @@ -41,72 +83,87 @@ pub struct OpenOptions { #[derive(Copy, Clone, Debug, Default)] pub struct FileTimes {} -pub struct FilePermissions(!); - -pub struct FileType(!); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + mode: u32, +} -#[derive(Debug)] -pub struct DirBuilder {} +#[derive(Copy, Clone, Eq, Debug)] +pub struct FileType { + mode: u32, +} -impl FileAttr { - pub fn size(&self) -> u64 { - self.0 +impl PartialEq for FileType { + fn eq(&self, other: &Self) -> bool { + self.mode == other.mode } +} - pub fn perm(&self) -> FilePermissions { - self.0 +impl core::hash::Hash for FileType { + fn hash<H: core::hash::Hasher>(&self, state: &mut H) { + self.mode.hash(state); } +} - pub fn file_type(&self) -> FileType { - self.0 - } +#[derive(Debug)] +pub struct DirBuilder { + mode: u32, +} +impl FileAttr { pub fn modified(&self) -> io::Result<SystemTime> { - self.0 + Ok(SystemTime::new( + self.stat_val.st_mtime.try_into().unwrap(), + self.stat_val.st_mtime_nsec.try_into().unwrap(), + )) } pub fn accessed(&self) -> io::Result<SystemTime> { - self.0 + Ok(SystemTime::new( + self.stat_val.st_atime.try_into().unwrap(), + self.stat_val.st_atime_nsec.try_into().unwrap(), + )) } pub fn created(&self) -> io::Result<SystemTime> { - self.0 + Ok(SystemTime::new( + self.stat_val.st_ctime.try_into().unwrap(), + self.stat_val.st_ctime_nsec.try_into().unwrap(), + )) } -} -impl Clone for FileAttr { - fn clone(&self) -> FileAttr { - self.0 + pub fn size(&self) -> u64 { + self.stat_val.st_size as u64 } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.0 + pub fn perm(&self) -> FilePermissions { + FilePermissions { mode: (self.stat_val.st_mode) } } - pub fn set_readonly(&mut self, _readonly: bool) { - self.0 + pub fn file_type(&self) -> FileType { + let masked_mode = self.stat_val.st_mode & S_IFMT; + let mode = match masked_mode { + S_IFDIR => DT_DIR, + S_IFLNK => DT_LNK, + S_IFREG => DT_REG, + _ => DT_UNKNOWN, + }; + FileType { mode: mode } } } -impl Clone for FilePermissions { - fn clone(&self) -> FilePermissions { - self.0 +impl FilePermissions { + pub fn readonly(&self) -> bool { + // check if any class (owner, group, others) has write permission + self.mode & 0o222 == 0 } -} -impl PartialEq for FilePermissions { - fn eq(&self, _other: &FilePermissions) -> bool { - self.0 + pub fn set_readonly(&mut self, _readonly: bool) { + unimplemented!() } -} - -impl Eq for FilePermissions {} -impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + #[allow(dead_code)] + pub fn mode(&self) -> u32 { + self.mode as u32 } } @@ -117,49 +174,21 @@ impl FileTimes { impl FileType { pub fn is_dir(&self) -> bool { - self.0 + self.mode == DT_DIR } - pub fn is_file(&self) -> bool { - self.0 + self.mode == DT_REG } - pub fn is_symlink(&self) -> bool { - self.0 - } -} - -impl Clone for FileType { - fn clone(&self) -> FileType { - self.0 - } -} - -impl Copy for FileType {} - -impl PartialEq for FileType { - fn eq(&self, _other: &FileType) -> bool { - self.0 - } -} - -impl Eq for FileType {} - -impl Hash for FileType { - fn hash<H: Hasher>(&self, _h: &mut H) { - self.0 - } -} - -impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + self.mode == DT_LNK } } impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. + // Thus the result will be e.g. 'ReadDir("/home")' + fmt::Debug::fmt(&*self.inner.root, f) } } @@ -167,25 +196,78 @@ impl Iterator for ReadDir { type Item = io::Result<DirEntry>; fn next(&mut self) -> Option<io::Result<DirEntry>> { - self.0 + let mut counter: usize = 0; + let mut offset: i64 = 0; + + // loop over all directory entries and search the entry for the current position + loop { + // leave function, if the loop reaches the of the buffer (with all entries) + if offset >= self.inner.dir.len().try_into().unwrap() { + return None; + } + + let dir = unsafe { + &*(self.inner.dir.as_ptr().offset(offset.try_into().unwrap()) as *const dirent64) + }; + + if counter == self.pos.try_into().unwrap() { + self.pos += 1; + + // After dirent64, the file name is stored. d_reclen represents the length of the dirent64 + // plus the length of the file name. Consequently, file name has a size of d_reclen minus + // the size of dirent64. The file name is always a C string and terminated by `\0`. + // Consequently, we are able to ignore the last byte. + let name_bytes = unsafe { + core::slice::from_raw_parts( + &dir.d_name as *const _ as *const u8, + dir.d_reclen as usize - core::mem::size_of::<dirent64>() - 1, + ) + .to_vec() + }; + let entry = DirEntry { + root: self.inner.root.clone(), + ino: dir.d_ino, + type_: dir.d_type as u32, + name: OsString::from_vec(name_bytes), + }; + + return Some(Ok(entry)); + } + + counter += 1; + + // move to the next dirent64, which is directly stored after the previous one + offset = offset + dir.d_off; + } } } impl DirEntry { pub fn path(&self) -> PathBuf { - self.0 + self.root.join(self.file_name_os_str()) } pub fn file_name(&self) -> OsString { - self.0 + self.file_name_os_str().to_os_string() } pub fn metadata(&self) -> io::Result<FileAttr> { - self.0 + let mut path = self.path(); + path.set_file_name(self.file_name_os_str()); + lstat(&path) } pub fn file_type(&self) -> io::Result<FileType> { - self.0 + Ok(FileType { mode: self.type_ as u32 }) + } + + #[allow(dead_code)] + pub fn ino(&self) -> u64 { + self.ino + } + + pub fn file_name_os_str(&self) -> &OsStr { + self.name.as_os_str() } } @@ -288,7 +370,9 @@ impl File { } pub fn file_attr(&self) -> io::Result<FileAttr> { - Err(Error::from_raw_os_error(22)) + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + self.0.fstat(&mut stat_val)?; + Ok(FileAttr::from_stat(stat_val)) } pub fn fsync(&self) -> io::Result<()> { @@ -357,11 +441,18 @@ impl File { impl DirBuilder { pub fn new() -> DirBuilder { - DirBuilder {} + DirBuilder { mode: 0o777 } } - pub fn mkdir(&self, _p: &Path) -> io::Result<()> { - unsupported() + pub fn mkdir(&self, path: &Path) -> io::Result<()> { + run_path_with_cstr(path, &|path| { + cvt(unsafe { abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ()) + }) + } + + #[allow(dead_code)] + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as u32; } } @@ -416,8 +507,43 @@ impl FromRawFd for File { } } -pub fn readdir(_p: &Path) -> io::Result<ReadDir> { - unsupported() +pub fn readdir(path: &Path) -> io::Result<ReadDir> { + let fd_raw = run_path_with_cstr(path, &|path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?; + let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; + let root = path.to_path_buf(); + + // read all director entries + let mut vec: Vec<u8> = Vec::new(); + let mut sz = 512; + loop { + // reserve memory to receive all directory entries + vec.resize(sz, 0); + + let readlen = + unsafe { abi::getdents64(fd.as_raw_fd(), vec.as_mut_ptr() as *mut dirent64, sz) }; + if readlen > 0 { + // shrink down to the minimal size + vec.resize(readlen.try_into().unwrap(), 0); + break; + } + + // if the buffer is too small, getdents64 returns EINVAL + // otherwise, getdents64 returns an error number + if readlen != (-abi::errno::EINVAL).into() { + return Err(Error::from_raw_os_error(readlen.try_into().unwrap())); + } + + // we don't have enough memory => try to increase the vector size + sz = sz * 2; + + // 1 MB for directory entries should be enough + // stop here to avoid an endless loop + if sz > 0x100000 { + return Err(Error::from(ErrorKind::Uncategorized)); + } + } + + Ok(ReadDir::new(InnerReadDir::new(root, vec))) } pub fn unlink(path: &Path) -> io::Result<()> { @@ -428,17 +554,16 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { unsupported() } -pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { - match perm.0 {} +pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) } -pub fn rmdir(_p: &Path) -> io::Result<()> { - unsupported() +pub fn rmdir(path: &Path) -> io::Result<()> { + run_path_with_cstr(path, &|path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ())) } pub fn remove_dir_all(_path: &Path) -> io::Result<()> { - //unsupported() - Ok(()) + unsupported() } pub fn readlink(_p: &Path) -> io::Result<PathBuf> { @@ -453,12 +578,20 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { unsupported() } -pub fn stat(_p: &Path) -> io::Result<FileAttr> { - unsupported() +pub fn stat(path: &Path) -> io::Result<FileAttr> { + run_path_with_cstr(path, &|path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { abi::stat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) } -pub fn lstat(_p: &Path) -> io::Result<FileAttr> { - unsupported() +pub fn lstat(path: &Path) -> io::Result<FileAttr> { + run_path_with_cstr(path, &|path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { abi::lstat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) } pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> { diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index cf45b9c2396..4fe6b12a95b 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -2,7 +2,7 @@ use super::abi; use super::thread_local_dtor::run_dtors; -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::mem; use crate::num::NonZero; @@ -29,7 +29,7 @@ impl Thread { let p = Box::into_raw(Box::new(p)); let tid = abi::spawn2( thread_start, - p.expose_addr(), + p.expose_provenance(), abi::Priority::into(abi::NORMAL_PRIO), stack, core_id, @@ -47,7 +47,7 @@ impl Thread { extern "C" fn thread_start(main: usize) { unsafe { // Finally, let's run some code. - Box::from_raw(ptr::from_exposed_addr::<Box<dyn FnOnce()>>(main).cast_mut())(); + Box::from_raw(ptr::with_exposed_provenance::<Box<dyn FnOnce()>>(main).cast_mut())(); // run all destructors run_dtors(); @@ -71,10 +71,6 @@ impl Thread { // nope } - pub fn get_name() -> Option<CString> { - None - } - #[inline] pub fn sleep(dur: Duration) { unsafe { @@ -104,13 +100,3 @@ impl Thread { pub fn available_parallelism() -> io::Result<NonZero<usize>> { unsafe { Ok(NonZero::new_unchecked(abi::get_processor_count())) } } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option<Guard> { - None - } - pub unsafe fn init() -> Option<Guard> { - None - } -} diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index 319b835a768..d6f9e4c1476 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -18,6 +18,12 @@ impl Timespec { Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } } + const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); + // SAFETY: The assert above checks tv_nsec is within the valid range + Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } } + } + fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { if self >= other { Ok(if self.t.tv_nsec >= other.t.tv_nsec { @@ -195,6 +201,10 @@ pub struct SystemTime(Timespec); pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); impl SystemTime { + pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + SystemTime(Timespec::new(tv_sec, tv_nsec)) + } + pub fn now() -> SystemTime { let mut time: Timespec = Timespec::zero(); let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) }; diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index 814a102dd09..205226ce1da 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -8,7 +8,7 @@ use super::{ }; use crate::{ cell::UnsafeCell, - ffi::{CStr, CString}, + ffi::CStr, hint, io, mem::ManuallyDrop, num::NonZero, @@ -98,7 +98,7 @@ impl Thread { }); unsafe extern "C" fn trampoline(exinf: isize) { - let p_inner: *mut ThreadInner = crate::ptr::from_exposed_addr_mut(exinf as usize); + let p_inner: *mut ThreadInner = crate::ptr::with_exposed_provenance_mut(exinf as usize); // Safety: `ThreadInner` is alive at this point let inner = unsafe { &*p_inner }; @@ -181,7 +181,7 @@ impl Thread { abi::acre_tsk(&abi::T_CTSK { // Activate this task immediately tskatr: abi::TA_ACT, - exinf: p_inner.as_ptr().expose_addr() as abi::EXINF, + exinf: p_inner.as_ptr().expose_provenance() as abi::EXINF, // The entry point task: Some(trampoline), // Inherit the calling task's base priority @@ -204,10 +204,6 @@ impl Thread { // nope } - pub fn get_name() -> Option<CString> { - None - } - pub fn sleep(dur: Duration) { for timeout in dur2reltims(dur) { expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk"); @@ -312,16 +308,6 @@ impl Drop for Thread { } } -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option<Guard> { - None - } - pub unsafe fn init() -> Option<Guard> { - None - } -} - /// Terminate and delete the specified task. /// /// This function will abort if `deleted_task` refers to the calling task. diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 7c87deed371..8c75ac65299 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -37,12 +37,12 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "hermit")] { mod hermit; pub use self::hermit::*; - } else if #[cfg(target_os = "wasi")] { - mod wasi; - pub use self::wasi::*; } else if #[cfg(all(target_os = "wasi", target_env = "p2"))] { mod wasip2; pub use self::wasip2::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use self::wasi::*; } else if #[cfg(target_family = "wasm")] { mod wasm; pub use self::wasm::*; diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/pal/sgx/net.rs index c4d5da1627c..edb28e2300f 100644 --- a/library/std/src/sys/pal/sgx/net.rs +++ b/library/std/src/sys/pal/sgx/net.rs @@ -524,6 +524,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -536,6 +537,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index 77f68bf7334..e2df57b1a1f 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -1,6 +1,6 @@ #![cfg_attr(test, allow(dead_code))] // why is this necessary? use super::unsupported; -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::time::Duration; @@ -133,10 +133,6 @@ impl Thread { // which succeeds as-is with the SGX target. } - pub fn get_name() -> Option<CString> { - None - } - pub fn sleep(dur: Duration) { usercalls::wait_timeout(0, dur, || true); } @@ -149,13 +145,3 @@ impl Thread { pub fn available_parallelism() -> io::Result<NonZero<usize>> { unsupported() } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option<Guard> { - None - } - pub unsafe fn init() -> Option<Guard> { - None - } -} diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs index b76bcf9bbb0..ae2f58ca08e 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -1,7 +1,7 @@ use core::convert::TryInto; use crate::cmp; -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::mem; use crate::num::NonZero; @@ -101,10 +101,6 @@ impl Thread { // contact the teeos rustzone team. } - pub fn get_name() -> Option<CString> { - None - } - /// only main thread could wait for sometime in teeos pub fn sleep(dur: Duration) { let sleep_millis = dur.as_millis(); @@ -151,18 +147,6 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { )) } -// stub -pub mod guard { - use crate::ops::Range; - pub type Guard = Range<usize>; - pub unsafe fn current() -> Option<Guard> { - None - } - pub unsafe fn init() -> Option<Guard> { - None - } -} - fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { libc::PTHREAD_STACK_MIN.try_into().expect("Infallible") } diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index ba53ed88f37..23aa4da14a7 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -10,14 +10,16 @@ //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) use r_efi::efi::{self, Guid}; +use r_efi::protocols::{device_path, device_path_to_text}; +use crate::ffi::OsString; +use crate::io::{self, const_io_error}; use crate::mem::{size_of, MaybeUninit}; -use crate::os::uefi; +use crate::os::uefi::{self, env::boot_services, ffi::OsStringExt}; use crate::ptr::NonNull; -use crate::{ - io::{self, const_io_error}, - os::uefi::env::boot_services, -}; +use crate::slice; +use crate::sync::atomic::{AtomicPtr, Ordering}; +use crate::sys_common::wstr::WStrUnits; const BOOT_SERVICES_UNAVAILABLE: io::Error = const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); @@ -142,9 +144,74 @@ pub(crate) unsafe fn close_event(evt: NonNull<crate::ffi::c_void>) -> io::Result /// Get the Protocol for current system handle. /// Note: Some protocols need to be manually freed. It is the callers responsibility to do so. -pub(crate) fn image_handle_protocol<T>(protocol_guid: Guid) -> Option<NonNull<T>> { - let system_handle = uefi::env::try_image_handle()?; - open_protocol(system_handle, protocol_guid).ok() +pub(crate) fn image_handle_protocol<T>(protocol_guid: Guid) -> io::Result<NonNull<T>> { + let system_handle = uefi::env::try_image_handle().ok_or(io::const_io_error!( + io::ErrorKind::NotFound, + "Protocol not found in Image handle" + ))?; + open_protocol(system_handle, protocol_guid) +} + +pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::Result<OsString> { + fn path_to_text( + protocol: NonNull<device_path_to_text::Protocol>, + path: NonNull<device_path::Protocol>, + ) -> io::Result<OsString> { + let path_ptr: *mut r_efi::efi::Char16 = unsafe { + ((*protocol.as_ptr()).convert_device_path_to_text)( + path.as_ptr(), + // DisplayOnly + r_efi::efi::Boolean::FALSE, + // AllowShortcuts + r_efi::efi::Boolean::FALSE, + ) + }; + + // SAFETY: `convert_device_path_to_text` returns a pointer to a null-terminated UTF-16 + // string, and that string cannot be deallocated prior to dropping the `WStrUnits`, so + // it's safe for `WStrUnits` to use. + let path_len = unsafe { + WStrUnits::new(path_ptr) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))? + .count() + }; + + let path = OsString::from_wide(unsafe { slice::from_raw_parts(path_ptr.cast(), path_len) }); + + if let Some(boot_services) = crate::os::uefi::env::boot_services() { + let boot_services: NonNull<r_efi::efi::BootServices> = boot_services.cast(); + unsafe { + ((*boot_services.as_ptr()).free_pool)(path_ptr.cast()); + } + } + + Ok(path) + } + + static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> = + AtomicPtr::new(crate::ptr::null_mut()); + + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { + if let Ok(protocol) = open_protocol::<device_path_to_text::Protocol>( + handle, + device_path_to_text::PROTOCOL_GUID, + ) { + return path_to_text(protocol, path); + } + } + + let device_path_to_text_handles = locate_handles(device_path_to_text::PROTOCOL_GUID)?; + for handle in device_path_to_text_handles { + if let Ok(protocol) = open_protocol::<device_path_to_text::Protocol>( + handle, + device_path_to_text::PROTOCOL_GUID, + ) { + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); + return path_to_text(protocol, path); + } + } + + Err(io::const_io_error!(io::ErrorKind::NotFound, "No device path to text protocol found")) } /// Get RuntimeServices diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index e6693db68e6..58838c5876e 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -1,4 +1,4 @@ -use super::{unsupported, RawOsError}; +use super::{helpers, unsupported, RawOsError}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::fmt; @@ -7,6 +7,7 @@ use crate::marker::PhantomData; use crate::os::uefi; use crate::path::{self, PathBuf}; use crate::ptr::NonNull; +use r_efi::efi::protocols::{device_path, loaded_image_device_path}; use r_efi::efi::Status; pub fn errno() -> RawOsError { @@ -164,7 +165,10 @@ impl fmt::Display for JoinPathsError { impl StdError for JoinPathsError {} pub fn current_exe() -> io::Result<PathBuf> { - unsupported() + let protocol = helpers::image_handle_protocol::<device_path::Protocol>( + loaded_image_device_path::PROTOCOL_GUID, + )?; + helpers::device_path_to_text(protocol).map(PathBuf::from) } pub struct Env(!); diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs index b3a4f9c53e3..edc736978a1 100644 --- a/library/std/src/sys/pal/uefi/thread.rs +++ b/library/std/src/sys/pal/uefi/thread.rs @@ -1,5 +1,5 @@ use super::unsupported; -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::ptr::NonNull; @@ -23,10 +23,6 @@ impl Thread { // nope } - pub fn get_name() -> Option<CString> { - None - } - pub fn sleep(dur: Duration) { let boot_services: NonNull<r_efi::efi::BootServices> = crate::os::uefi::env::boot_services().expect("can't sleep").cast(); @@ -52,13 +48,3 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { // UEFI is single threaded Ok(NonZero::new(1).unwrap()) } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option<Guard> { - None - } - pub unsafe fn init() -> Option<Guard> { - None - } -} diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs index 78e82d9c194..acf8100d47f 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/pal/unix/args.rs @@ -170,7 +170,13 @@ mod imp { } } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "visionos", + target_os = "tvos" +))] mod imp { use super::Args; use crate::ffi::CStr; @@ -211,7 +217,12 @@ mod imp { // for i in (0..[args count]) // res.push([args objectAtIndex:i]) // res - #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] + #[cfg(any( + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "visionos" + ))] pub fn args() -> Args { use crate::ffi::{c_char, c_void, OsString}; use crate::mem; diff --git a/library/std/src/sys/pal/unix/env.rs b/library/std/src/sys/pal/unix/env.rs index 3d4ba509829..fb1f868644d 100644 --- a/library/std/src/sys/pal/unix/env.rs +++ b/library/std/src/sys/pal/unix/env.rs @@ -53,6 +53,17 @@ pub mod os { pub const EXE_EXTENSION: &str = ""; } +#[cfg(target_os = "visionos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "visionos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + #[cfg(target_os = "freebsd")] 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 a1c0321876f..48e83b04ef4 100644 --- a/library/std/src/sys/pal/unix/fd.rs +++ b/library/std/src/sys/pal/unix/fd.rs @@ -51,6 +51,7 @@ const READ_LIMIT: usize = libc::ssize_t::MAX as usize; target_os = "netbsd", target_os = "openbsd", target_os = "watchos", + target_os = "visionos", ))] const fn max_iov() -> usize { libc::IOV_MAX as usize @@ -81,6 +82,7 @@ const fn max_iov() -> usize { target_os = "horizon", target_os = "vita", target_os = "watchos", + target_os = "visionos", )))] const fn max_iov() -> usize { 16 // The minimum value required by POSIX. @@ -167,6 +169,8 @@ impl FileDesc { } #[cfg(any( + target_os = "aix", + target_os = "dragonfly", // DragonFly 1.5 target_os = "emscripten", target_os = "freebsd", target_os = "fuchsia", @@ -174,6 +178,7 @@ impl FileDesc { target_os = "illumos", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", // OpenBSD 2.7 ))] pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { let ret = cvt(unsafe { @@ -188,7 +193,9 @@ impl FileDesc { } #[cfg(not(any( + target_os = "aix", target_os = "android", + target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", target_os = "fuchsia", @@ -199,6 +206,8 @@ impl FileDesc { target_os = "linux", target_os = "macos", target_os = "netbsd", + target_os = "openbsd", + target_os = "watchos", )))] pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { io::default_read_vectored(|b| self.read_at(b, offset), bufs) @@ -236,9 +245,10 @@ impl FileDesc { // no `syscall` possible in these platform. #[cfg(any( all(target_os = "android", target_pointer_width = "32"), - target_os = "ios", - target_os = "tvos", - target_os = "macos", + target_os = "ios", // ios 14.0 + target_os = "tvos", // tvos 14.0 + target_os = "macos", // macos 11.0 + target_os = "watchos", // watchos 7.0 ))] pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); @@ -318,6 +328,8 @@ impl FileDesc { } #[cfg(any( + target_os = "aix", + target_os = "dragonfly", // DragonFly 1.5 target_os = "emscripten", target_os = "freebsd", target_os = "fuchsia", @@ -325,6 +337,7 @@ impl FileDesc { target_os = "illumos", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", // OpenBSD 2.7 ))] pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { let ret = cvt(unsafe { @@ -339,7 +352,9 @@ impl FileDesc { } #[cfg(not(any( + target_os = "aix", target_os = "android", + target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", target_os = "fuchsia", @@ -350,6 +365,8 @@ impl FileDesc { target_os = "linux", target_os = "macos", target_os = "netbsd", + target_os = "openbsd", + target_os = "watchos", )))] pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { io::default_write_vectored(|b| self.write_at(b, offset), bufs) @@ -387,9 +404,10 @@ impl FileDesc { // no `syscall` possible in these platform. #[cfg(any( all(target_os = "android", target_pointer_width = "32"), - target_os = "ios", - target_os = "tvos", - target_os = "macos", + target_os = "ios", // ios 14.0 + target_os = "tvos", // tvos 14.0 + target_os = "macos", // macos 11.0 + target_os = "watchos", // watchos 7.0 ))] pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index df8a92114ec..e8430b6ae70 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -26,6 +26,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", ))] use crate::sys::weak::syscall; #[cfg(any(target_os = "android", target_os = "macos", target_os = "solaris"))] @@ -38,6 +39,7 @@ use libc::{c_int, mode_t}; target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "solaris", all(target_os = "linux", target_env = "gnu") ))] @@ -380,7 +382,13 @@ pub struct FilePermissions { pub struct FileTimes { accessed: Option<SystemTime>, modified: Option<SystemTime>, - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "visionos", + target_os = "tvos" + ))] created: Option<SystemTime>, } @@ -522,7 +530,7 @@ impl FileAttr { #[cfg(any(target_os = "horizon", target_os = "hurd"))] pub fn modified(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(self.stat.st_mtim)) + SystemTime::new(self.stat.st_mtim.tv_sec as i64, self.stat.st_mtim.tv_nsec as i64) } #[cfg(not(any( @@ -550,7 +558,7 @@ impl FileAttr { #[cfg(any(target_os = "horizon", target_os = "hurd"))] pub fn accessed(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(self.stat.st_atim)) + SystemTime::new(self.stat.st_atim.tv_sec as i64, self.stat.st_atim.tv_nsec as i64) } #[cfg(any( @@ -560,6 +568,7 @@ impl FileAttr { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", ))] pub fn created(&self) -> io::Result<SystemTime> { SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64) @@ -572,6 +581,7 @@ impl FileAttr { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "vita", )))] pub fn created(&self) -> io::Result<SystemTime> { @@ -652,7 +662,13 @@ impl FileTimes { self.modified = Some(t); } - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "visionos", + target_os = "tvos" + ))] pub fn set_created(&mut self, t: SystemTime) { self.created = Some(t); } @@ -957,6 +973,7 @@ impl DirEntry { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "linux", target_os = "emscripten", target_os = "android", @@ -993,6 +1010,7 @@ impl DirEntry { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "netbsd", target_os = "openbsd", target_os = "freebsd", @@ -1012,6 +1030,7 @@ impl DirEntry { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "netbsd", target_os = "openbsd", target_os = "freebsd", @@ -1198,6 +1217,7 @@ impl File { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", ))] unsafe fn os_fsync(fd: c_int) -> c_int { libc::fcntl(fd, libc::F_FULLFSYNC) @@ -1207,6 +1227,7 @@ impl File { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", )))] unsafe fn os_fsync(fd: c_int) -> c_int { libc::fsync(fd) @@ -1222,6 +1243,7 @@ impl File { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", ))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fcntl(fd, libc::F_FULLFSYNC) @@ -1248,6 +1270,7 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "watchos", + target_os = "visionos", target_os = "nto", target_os = "hurd", )))] @@ -1358,7 +1381,7 @@ impl File { io::ErrorKind::Unsupported, "setting file times not supported", )) - } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] { + } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] { let mut buf = [mem::MaybeUninit::<libc::timespec>::uninit(); 3]; let mut num_times = 0; let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; @@ -1897,6 +1920,7 @@ fn open_to_and_set_permissions( target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", )))] pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { let (mut reader, reader_metadata) = open_from(from)?; @@ -1923,43 +1947,19 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { } } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "visionos", + target_os = "tvos" +))] pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { use crate::sync::atomic::{AtomicBool, Ordering}; - const COPYFILE_ACL: u32 = 1 << 0; - const COPYFILE_STAT: u32 = 1 << 1; - const COPYFILE_XATTR: u32 = 1 << 2; - const COPYFILE_DATA: u32 = 1 << 3; - - const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL; - const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR; - const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA; - - const COPYFILE_STATE_COPIED: u32 = 8; - - #[allow(non_camel_case_types)] - type copyfile_state_t = *mut libc::c_void; - #[allow(non_camel_case_types)] - type copyfile_flags_t = u32; - - extern "C" { - fn fcopyfile( - from: libc::c_int, - to: libc::c_int, - state: copyfile_state_t, - flags: copyfile_flags_t, - ) -> libc::c_int; - fn copyfile_state_alloc() -> copyfile_state_t; - fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int; - fn copyfile_state_get( - state: copyfile_state_t, - flag: u32, - dst: *mut libc::c_void, - ) -> libc::c_int; - } - - struct FreeOnDrop(copyfile_state_t); + const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA; + + struct FreeOnDrop(libc::copyfile_state_t); impl Drop for FreeOnDrop { fn drop(&mut self) { // The code below ensures that `FreeOnDrop` is never a null pointer @@ -1967,7 +1967,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { // `copyfile_state_free` returns -1 if the `to` or `from` files // cannot be closed. However, this is not considered this an // error. - copyfile_state_free(self.0); + libc::copyfile_state_free(self.0); } } } @@ -1976,6 +1976,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { // We store the availability in a global to avoid unnecessary syscalls static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true); syscall! { + // Mirrors `libc::fclonefileat` fn fclonefileat( srcfd: libc::c_int, dst_dirfd: libc::c_int, @@ -2012,22 +2013,22 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { // We ensure that `FreeOnDrop` never contains a null pointer so it is // always safe to call `copyfile_state_free` let state = unsafe { - let state = copyfile_state_alloc(); + let state = libc::copyfile_state_alloc(); if state.is_null() { return Err(crate::io::Error::last_os_error()); } FreeOnDrop(state) }; - let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { COPYFILE_DATA }; + let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { libc::COPYFILE_DATA }; - cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; + cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; let mut bytes_copied: libc::off_t = 0; cvt(unsafe { - copyfile_state_get( + libc::copyfile_state_get( state.0, - COPYFILE_STATE_COPIED, + libc::COPYFILE_STATE_COPIED as u32, core::ptr::addr_of_mut!(bytes_copied) as *mut libc::c_void, ) })?; diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index f608ae47a21..4ae76518c4f 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -83,6 +83,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "redox", target_os = "l4re", target_os = "horizon", @@ -405,7 +406,7 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "macos")] { #[link(name = "System")] extern "C" {} - } else if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] { + } else if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] { #[link(name = "System")] #[link(name = "objc")] #[link(name = "Foundation", kind = "framework")] diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index 09750b6ffc8..9a0a1b18aee 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/pal/unix/net.rs @@ -453,6 +453,37 @@ impl Socket { Ok(raw as u32) } + #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] + pub fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + if !name.to_bytes().is_empty() { + const AF_NAME_MAX: usize = 16; + let mut buf = [0; AF_NAME_MAX]; + for (src, dst) in name.to_bytes().iter().zip(&mut buf[..AF_NAME_MAX - 1]) { + *dst = *src as libc::c_char; + } + let mut arg: libc::accept_filter_arg = unsafe { mem::zeroed() }; + arg.af_name = buf; + setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) + } else { + setsockopt( + self, + libc::SOL_SOCKET, + libc::SO_ACCEPTFILTER, + core::ptr::null_mut() as *mut c_void, + ) + } + } + + #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] + pub fn acceptfilter(&self) -> io::Result<&CStr> { + let arg: libc::accept_filter_arg = + getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)?; + let s: &[u8] = + unsafe { core::slice::from_raw_parts(arg.af_name.as_ptr() as *const u8, 16) }; + let name = CStr::from_bytes_with_nul(s).unwrap(); + Ok(name) + } + #[cfg(any(target_os = "android", target_os = "linux",))] pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 0b9c8027e6f..96492fedece 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -69,7 +69,8 @@ extern "C" { target_os = "ios", target_os = "tvos", target_os = "freebsd", - target_os = "watchos" + target_os = "watchos", + target_os = "visionos", ), link_name = "__error" )] @@ -430,7 +431,13 @@ pub fn current_exe() -> io::Result<PathBuf> { Ok(PathBuf::from(OsString::from_vec(e))) } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "visionos", + target_os = "tvos" +))] pub fn current_exe() -> io::Result<PathBuf> { unsafe { let mut sz: u32 = 0; @@ -699,6 +706,7 @@ pub fn home_dir() -> Option<PathBuf> { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "emscripten", target_os = "redox", target_os = "vxworks", @@ -714,6 +722,7 @@ pub fn home_dir() -> Option<PathBuf> { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "emscripten", target_os = "redox", target_os = "vxworks", 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 f017d39d804..e798510f9e6 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -14,6 +14,7 @@ use crate::os::unix::io::AsRawFd; #[cfg(any( target_os = "macos", target_os = "watchos", + target_os = "visionos", target_os = "tvos", target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), diff --git a/library/std/src/sys/pal/unix/rand.rs b/library/std/src/sys/pal/unix/rand.rs index c9ed6825f6c..de087d98eb8 100644 --- a/library/std/src/sys/pal/unix/rand.rs +++ b/library/std/src/sys/pal/unix/rand.rs @@ -16,6 +16,7 @@ pub fn hashmap_random_keys() -> (u64, u64) { not(target_os = "ios"), not(target_os = "tvos"), not(target_os = "watchos"), + not(target_os = "visionos"), not(target_os = "openbsd"), not(target_os = "netbsd"), not(target_os = "fuchsia"), @@ -62,17 +63,23 @@ mod imp { unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) } } - #[cfg(any( - target_os = "espidf", - target_os = "horizon", - target_os = "freebsd", - target_os = "dragonfly", - netbsd10 - ))] + #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "freebsd", netbsd10))] fn getrandom(buf: &mut [u8]) -> libc::ssize_t { unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } } + #[cfg(target_os = "dragonfly")] + fn getrandom(buf: &mut [u8]) -> libc::ssize_t { + extern "C" { + fn getrandom( + buf: *mut libc::c_void, + buflen: libc::size_t, + flags: libc::c_uint, + ) -> libc::ssize_t; + } + unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } + } + #[cfg(not(any( target_os = "linux", target_os = "android", diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 78a599077c7..26c49257ad0 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -11,7 +11,7 @@ pub struct Handler { impl Handler { pub unsafe fn new() -> Handler { - make_handler() + make_handler(false) } fn null() -> Handler { @@ -29,34 +29,41 @@ impl Drop for Handler { #[cfg(any( target_os = "linux", - target_os = "macos", - target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", - target_os = "solaris", - target_os = "illumos", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "solaris" ))] mod imp { use super::Handler; + use crate::cell::Cell; use crate::io; use crate::mem; + use crate::ops::Range; use crate::ptr; + use crate::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; + use crate::sys::pal::unix::os; use crate::thread; - use libc::MAP_FAILED; #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::{mmap as mmap64, munmap}; + use libc::{mmap as mmap64, mprotect, munmap}; #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{mmap64, munmap}; - use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL}; + use libc::{mmap64, mprotect, munmap}; + use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSEGV, SIG_DFL}; use libc::{sigaltstack, SS_DISABLE}; - use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV}; + use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; - use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; - use crate::sys::pal::unix::os::page_size; - use crate::sys_common::thread_info; + // We use a TLS variable to store the address of the guard page. While TLS + // variables are not guaranteed to be signal-safe, this works out in practice + // since we make sure to write to the variable before the signal stack is + // installed, thereby ensuring that the variable is always allocated when + // the signal handler is called. + thread_local! { + // FIXME: use `Range` once that implements `Copy`. + static GUARD: Cell<(usize, usize)> = const { Cell::new((0, 0)) }; + } // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages // (unmapped pages) at the end of every thread's stack, so if a thread ends @@ -84,12 +91,12 @@ mod imp { info: *mut libc::siginfo_t, _data: *mut libc::c_void, ) { - let guard = thread_info::stack_guard().unwrap_or(0..0); + let (start, end) = GUARD.get(); let addr = (*info).si_addr() as usize; // If the faulting address is within the guard page, then we print a // message saying so and abort. - if guard.start <= addr && addr < guard.end { + if start <= addr && addr < end { rtprintpanic!( "\nthread '{}' has overflowed its stack\n", thread::current().name().unwrap_or("<unknown>") @@ -105,10 +112,17 @@ mod imp { } } + static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); static MAIN_ALTSTACK: AtomicPtr<libc::c_void> = AtomicPtr::new(ptr::null_mut()); static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false); pub unsafe fn init() { + PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); + + // Always write to GUARD to ensure the TLS variable is allocated. + let guard = install_main_guard().unwrap_or(0..0); + GUARD.set((guard.start, guard.end)); + let mut action: sigaction = mem::zeroed(); for &signal in &[SIGSEGV, SIGBUS] { sigaction(signal, ptr::null_mut(), &mut action); @@ -121,7 +135,7 @@ mod imp { } } - let handler = make_handler(); + let handler = make_handler(true); MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); mem::forget(handler); } @@ -150,7 +164,7 @@ mod imp { let flags = MAP_PRIVATE | MAP_ANON; let sigstack_size = sigstack_size(); - let page_size = page_size(); + let page_size = PAGE_SIZE.load(Ordering::Relaxed); let stackp = mmap64( ptr::null_mut(), @@ -172,10 +186,17 @@ mod imp { libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size } } - pub unsafe fn make_handler() -> Handler { + pub unsafe fn make_handler(main_thread: bool) -> Handler { if !NEED_ALTSTACK.load(Ordering::Relaxed) { return Handler::null(); } + + if !main_thread { + // Always write to GUARD to ensure the TLS variable is allocated. + let guard = current_guard().unwrap_or(0..0); + GUARD.set((guard.start, guard.end)); + } + let mut stack = mem::zeroed(); sigaltstack(ptr::null(), &mut stack); // Configure alternate signal stack, if one is not already set. @@ -191,7 +212,7 @@ mod imp { pub unsafe fn drop_handler(data: *mut libc::c_void) { if !data.is_null() { let sigstack_size = sigstack_size(); - let page_size = page_size(); + let page_size = PAGE_SIZE.load(Ordering::Relaxed); let stack = libc::stack_t { ss_sp: ptr::null_mut(), ss_flags: SS_DISABLE, @@ -225,25 +246,266 @@ mod imp { fn sigstack_size() -> usize { libc::SIGSTKSZ } + + #[cfg(target_os = "solaris")] + 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); + Some(current_stack.ss_sp) + } + + #[cfg(target_os = "macos")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let th = libc::pthread_self(); + let stackptr = libc::pthread_get_stackaddr_np(th); + Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th))) + } + + #[cfg(target_os = "openbsd")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut current_stack: libc::stack_t = crate::mem::zeroed(); + assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0); + + let stack_ptr = current_stack.ss_sp; + let stackaddr = if libc::pthread_main_np() == 1 { + // main thread + stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) + } else { + // new thread + stack_ptr.addr() - current_stack.ss_size + }; + Some(stack_ptr.with_addr(stackaddr)) + } + + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + target_os = "hurd", + target_os = "linux", + target_os = "l4re" + ))] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut ret = None; + let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); + #[cfg(target_os = "freebsd")] + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + #[cfg(target_os = "freebsd")] + let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); + #[cfg(not(target_os = "freebsd"))] + let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); + if e == 0 { + let mut stackaddr = crate::ptr::null_mut(); + let mut stacksize = 0; + assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0); + ret = Some(stackaddr); + } + if e == 0 || cfg!(target_os = "freebsd") { + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + } + ret + } + + unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> { + let page_size = PAGE_SIZE.load(Ordering::Relaxed); + let stackptr = get_stack_start()?; + let stackaddr = stackptr.addr(); + + // Ensure stackaddr is page aligned! A parent process might + // have reset RLIMIT_STACK to be non-page aligned. The + // pthread_attr_getstack() reports the usable stack area + // stackaddr < stackaddr + stacksize, so if stackaddr is not + // page-aligned, calculate the fix such that stackaddr < + // new_page_aligned_stackaddr < stackaddr + stacksize + let remainder = stackaddr % page_size; + Some(if remainder == 0 { + stackptr + } else { + stackptr.with_addr(stackaddr + page_size - remainder) + }) + } + + unsafe fn install_main_guard() -> Option<Range<usize>> { + let page_size = PAGE_SIZE.load(Ordering::Relaxed); + if cfg!(all(target_os = "linux", not(target_env = "musl"))) { + // Linux doesn't allocate the whole stack right away, and + // the kernel has its own stack-guard mechanism to fault + // when growing too close to an existing mapping. If we map + // our own guard, then the kernel starts enforcing a rather + // large gap above that, rendering much of the possible + // stack space useless. See #43052. + // + // Instead, we'll just note where we expect rlimit to start + // faulting, so our handler can report "stack overflow", and + // trust that the kernel's own stack guard will work. + let stackptr = get_stack_start_aligned()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - page_size..stackaddr) + } else if cfg!(all(target_os = "linux", target_env = "musl")) { + // For the main thread, the musl's pthread_attr_getstack + // returns the current stack size, rather than maximum size + // it can eventually grow to. It cannot be used to determine + // the position of kernel's stack guard. + None + } else if cfg!(target_os = "freebsd") { + // FreeBSD's stack autogrows, and optionally includes a guard page + // at the bottom. If we try to remap the bottom of the stack + // ourselves, FreeBSD's guard page moves upwards. So we'll just use + // the builtin guard page. + let stackptr = get_stack_start_aligned()?; + let guardaddr = stackptr.addr(); + // Technically the number of guard pages is tunable and controlled + // by the security.bsd.stack_guard_page sysctl. + // By default it is 1, checking once is enough since it is + // a boot time config value. + static PAGES: crate::sync::OnceLock<usize> = crate::sync::OnceLock::new(); + + let pages = PAGES.get_or_init(|| { + use crate::sys::weak::dlsym; + dlsym!(fn sysctlbyname(*const libc::c_char, *mut libc::c_void, *mut libc::size_t, *const libc::c_void, libc::size_t) -> libc::c_int); + let mut guard: usize = 0; + let mut size = crate::mem::size_of_val(&guard); + let oid = crate::ffi::CStr::from_bytes_with_nul( + b"security.bsd.stack_guard_page\0", + ) + .unwrap(); + match sysctlbyname.get() { + Some(fcn) => { + if fcn(oid.as_ptr(), core::ptr::addr_of_mut!(guard) as *mut _, core::ptr::addr_of_mut!(size) as *mut _, crate::ptr::null_mut(), 0) == 0 { + guard + } else { + 1 + } + }, + _ => 1, + } + }); + Some(guardaddr..guardaddr + pages * page_size) + } else if cfg!(any(target_os = "openbsd", target_os = "netbsd")) { + // OpenBSD stack already includes a guard page, and stack is + // immutable. + // NetBSD stack includes the guard page. + // + // We'll just note where we expect rlimit to start + // faulting, so our handler can report "stack overflow", and + // trust that the kernel's own stack guard will work. + let stackptr = get_stack_start_aligned()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - page_size..stackaddr) + } else { + // Reallocate the last page of the stack. + // This ensures SIGBUS will be raised on + // stack overflow. + // Systems which enforce strict PAX MPROTECT do not allow + // to mprotect() a mapping with less restrictive permissions + // than the initial mmap() used, so we mmap() here with + // read/write permissions and only then mprotect() it to + // no permissions at all. See issue #50313. + let stackptr = get_stack_start_aligned()?; + let result = mmap64( + stackptr, + page_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, + 0, + ); + if result != stackptr || result == MAP_FAILED { + panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); + } + + let result = mprotect(stackptr, page_size, PROT_NONE); + if result != 0 { + panic!("failed to protect the guard page: {}", io::Error::last_os_error()); + } + + let guardaddr = stackptr.addr(); + + Some(guardaddr..guardaddr + page_size) + } + } + + #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] + unsafe fn current_guard() -> Option<Range<usize>> { + let stackptr = get_stack_start()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr) + } + + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "l4re" + ))] + unsafe fn current_guard() -> Option<Range<usize>> { + let mut ret = None; + let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); + #[cfg(target_os = "freebsd")] + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + #[cfg(target_os = "freebsd")] + let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); + #[cfg(not(target_os = "freebsd"))] + let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); + if e == 0 { + let mut guardsize = 0; + assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0); + if guardsize == 0 { + if cfg!(all(target_os = "linux", target_env = "musl")) { + // musl versions before 1.1.19 always reported guard + // size obtained from pthread_attr_get_np as zero. + // Use page size as a fallback. + guardsize = PAGE_SIZE.load(Ordering::Relaxed); + } else { + panic!("there is no guard page"); + } + } + let mut stackptr = crate::ptr::null_mut::<libc::c_void>(); + let mut size = 0; + assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackptr, &mut size), 0); + + let stackaddr = stackptr.addr(); + ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { + Some(stackaddr - guardsize..stackaddr) + } else if cfg!(all(target_os = "linux", target_env = "musl")) { + Some(stackaddr - guardsize..stackaddr) + } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) + { + // glibc used to include the guard area within the stack, as noted in the BUGS + // section of `man pthread_attr_getguardsize`. This has been corrected starting + // with glibc 2.27, and in some distro backports, so the guard is now placed at the + // end (below) the stack. There's no easy way for us to know which we have at + // runtime, so we'll just match any fault in the range right above or below the + // stack base to call that fault a stack overflow. + Some(stackaddr - guardsize..stackaddr + guardsize) + } else { + Some(stackaddr..stackaddr + guardsize) + }; + } + if e == 0 || cfg!(target_os = "freebsd") { + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + } + ret + } } #[cfg(not(any( target_os = "linux", - target_os = "macos", - target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", - target_os = "solaris", - target_os = "illumos", + target_os = "macos", target_os = "netbsd", target_os = "openbsd", + target_os = "solaris" )))] mod imp { pub unsafe fn init() {} pub unsafe fn cleanup() {} - pub unsafe fn make_handler() -> super::Handler { + pub unsafe fn make_handler(_main_thread: bool) -> super::Handler { super::Handler::null() } diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 6520ca9fc48..f56e64c3505 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -1,5 +1,5 @@ use crate::cmp; -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::mem; use crate::num::NonZero; @@ -150,7 +150,13 @@ impl Thread { } } - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "visionos", + target_os = "tvos" + ))] pub fn set_name(name: &CStr) { unsafe { let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name); @@ -182,8 +188,11 @@ impl Thread { if let Some(f) = pthread_setname_np.get() { #[cfg(target_os = "nto")] - let name = truncate_cstr::<{ libc::_NTO_THREAD_NAME_MAX as usize }>(name); + const THREAD_NAME_MAX: usize = libc::_NTO_THREAD_NAME_MAX as usize; + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + const THREAD_NAME_MAX: usize = 32; + let name = truncate_cstr::<{ THREAD_NAME_MAX }>(name); let res = unsafe { f(libc::pthread_self(), name.as_ptr()) }; debug_assert_eq!(res, 0); } @@ -225,44 +234,6 @@ impl Thread { // Newlib, Emscripten, and VxWorks have no way to set a thread name. } - #[cfg(target_os = "linux")] - pub fn get_name() -> Option<CString> { - const TASK_COMM_LEN: usize = 16; - let mut name = vec![0u8; TASK_COMM_LEN]; - let res = unsafe { - libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len()) - }; - if res != 0 { - return None; - } - name.truncate(name.iter().position(|&c| c == 0)?); - CString::new(name).ok() - } - - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] - pub fn get_name() -> Option<CString> { - let mut name = vec![0u8; libc::MAXTHREADNAMESIZE]; - let res = unsafe { - libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len()) - }; - if res != 0 { - return None; - } - name.truncate(name.iter().position(|&c| c == 0)?); - CString::new(name).ok() - } - - #[cfg(not(any( - target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos" - )))] - pub fn get_name() -> Option<CString> { - None - } - #[cfg(not(target_os = "espidf"))] pub fn sleep(dur: Duration) { let mut secs = dur.as_secs(); @@ -334,7 +305,10 @@ impl Drop for Thread { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "nto", + target_os = "solaris", + target_os = "illumos", ))] fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { let mut result = [0; MAX_WITH_NUL]; @@ -355,8 +329,6 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { target_os = "tvos", target_os = "linux", target_os = "macos", - target_os = "solaris", - target_os = "illumos", target_os = "aix", ))] { #[allow(unused_assignments)] @@ -426,7 +398,7 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { if !set.is_null() { let mut count: usize = 0; if libc::pthread_getaffinity_np(libc::pthread_self(), libc::_cpuset_size(set), set) == 0 { - for i in 0..u64::MAX { + for i in 0..libc::cpuid_t::MAX { match libc::_cpuset_isset(i, set) { -1 => break, 0 => continue, @@ -483,6 +455,12 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { .ok_or(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")) } } + } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { + let mut cpus = 0u32; + if unsafe { libc::pset_info(libc::PS_MYID, core::ptr::null_mut(), &mut cpus, core::ptr::null_mut()) } != 0 { + return Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")); + } + Ok(unsafe { NonZero::new_unchecked(cpus as usize) }) } else if #[cfg(target_os = "haiku")] { // system_info cpu_count field gets the static data set at boot time with `smp_set_num_cpus` // `get_system_info` calls then `smp_get_num_cpus` @@ -725,302 +703,6 @@ mod cgroups { } } -#[cfg(all( - not(target_os = "linux"), - not(target_os = "freebsd"), - not(target_os = "hurd"), - not(target_os = "macos"), - not(target_os = "netbsd"), - not(target_os = "openbsd"), - not(target_os = "solaris") -))] -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - use crate::ops::Range; - pub type Guard = Range<usize>; - pub unsafe fn current() -> Option<Guard> { - None - } - pub unsafe fn init() -> Option<Guard> { - None - } -} - -#[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris" -))] -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::{mmap as mmap64, mprotect}; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{mmap64, mprotect}; - use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; - - use crate::io; - use crate::ops::Range; - use crate::sync::atomic::{AtomicUsize, Ordering}; - use crate::sys::os; - - // This is initialized in init() and only read from after - static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); - - pub type Guard = Range<usize>; - - #[cfg(target_os = "solaris")] - 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); - Some(current_stack.ss_sp) - } - - #[cfg(target_os = "macos")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let th = libc::pthread_self(); - let stackptr = libc::pthread_get_stackaddr_np(th); - Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th))) - } - - #[cfg(target_os = "openbsd")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut current_stack: libc::stack_t = crate::mem::zeroed(); - assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0); - - let stack_ptr = current_stack.ss_sp; - let stackaddr = if libc::pthread_main_np() == 1 { - // main thread - stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) - } else { - // new thread - stack_ptr.addr() - current_stack.ss_size - }; - Some(stack_ptr.with_addr(stackaddr)) - } - - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "netbsd", - target_os = "hurd", - target_os = "linux", - target_os = "l4re" - ))] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut ret = None; - let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); - #[cfg(target_os = "freebsd")] - assert_eq!(libc::pthread_attr_init(&mut attr), 0); - #[cfg(target_os = "freebsd")] - let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); - #[cfg(not(target_os = "freebsd"))] - let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); - if e == 0 { - let mut stackaddr = crate::ptr::null_mut(); - let mut stacksize = 0; - assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0); - ret = Some(stackaddr); - } - if e == 0 || cfg!(target_os = "freebsd") { - assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); - } - ret - } - - // Precondition: PAGE_SIZE is initialized. - unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> { - let page_size = PAGE_SIZE.load(Ordering::Relaxed); - assert!(page_size != 0); - let stackptr = get_stack_start()?; - let stackaddr = stackptr.addr(); - - // Ensure stackaddr is page aligned! A parent process might - // have reset RLIMIT_STACK to be non-page aligned. The - // pthread_attr_getstack() reports the usable stack area - // stackaddr < stackaddr + stacksize, so if stackaddr is not - // page-aligned, calculate the fix such that stackaddr < - // new_page_aligned_stackaddr < stackaddr + stacksize - let remainder = stackaddr % page_size; - Some(if remainder == 0 { - stackptr - } else { - stackptr.with_addr(stackaddr + page_size - remainder) - }) - } - - pub unsafe fn init() -> Option<Guard> { - let page_size = os::page_size(); - PAGE_SIZE.store(page_size, Ordering::Relaxed); - - if cfg!(all(target_os = "linux", not(target_env = "musl"))) { - // Linux doesn't allocate the whole stack right away, and - // the kernel has its own stack-guard mechanism to fault - // when growing too close to an existing mapping. If we map - // our own guard, then the kernel starts enforcing a rather - // large gap above that, rendering much of the possible - // stack space useless. See #43052. - // - // Instead, we'll just note where we expect rlimit to start - // faulting, so our handler can report "stack overflow", and - // trust that the kernel's own stack guard will work. - let stackptr = get_stack_start_aligned()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - page_size..stackaddr) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { - // For the main thread, the musl's pthread_attr_getstack - // returns the current stack size, rather than maximum size - // it can eventually grow to. It cannot be used to determine - // the position of kernel's stack guard. - None - } else if cfg!(target_os = "freebsd") { - // FreeBSD's stack autogrows, and optionally includes a guard page - // at the bottom. If we try to remap the bottom of the stack - // ourselves, FreeBSD's guard page moves upwards. So we'll just use - // the builtin guard page. - let stackptr = get_stack_start_aligned()?; - let guardaddr = stackptr.addr(); - // Technically the number of guard pages is tunable and controlled - // by the security.bsd.stack_guard_page sysctl. - // By default it is 1, checking once is enough since it is - // a boot time config value. - static LOCK: crate::sync::OnceLock<usize> = crate::sync::OnceLock::new(); - let guard = guardaddr - ..guardaddr - + *LOCK.get_or_init(|| { - use crate::sys::weak::dlsym; - dlsym!(fn sysctlbyname(*const libc::c_char, *mut libc::c_void, *mut libc::size_t, *const libc::c_void, libc::size_t) -> libc::c_int); - let mut guard: usize = 0; - let mut size = crate::mem::size_of_val(&guard); - let oid = crate::ffi::CStr::from_bytes_with_nul( - b"security.bsd.stack_guard_page\0", - ) - .unwrap(); - match sysctlbyname.get() { - Some(fcn) => { - if fcn(oid.as_ptr(), core::ptr::addr_of_mut!(guard) as *mut _, core::ptr::addr_of_mut!(size) as *mut _, crate::ptr::null_mut(), 0) == 0 { - return guard; - } - return 1; - }, - _ => { return 1; } - } - }) * page_size; - Some(guard) - } else if cfg!(any(target_os = "openbsd", target_os = "netbsd")) { - // OpenBSD stack already includes a guard page, and stack is - // immutable. - // NetBSD stack includes the guard page. - // - // We'll just note where we expect rlimit to start - // faulting, so our handler can report "stack overflow", and - // trust that the kernel's own stack guard will work. - let stackptr = get_stack_start_aligned()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - page_size..stackaddr) - } else { - // Reallocate the last page of the stack. - // This ensures SIGBUS will be raised on - // stack overflow. - // Systems which enforce strict PAX MPROTECT do not allow - // to mprotect() a mapping with less restrictive permissions - // than the initial mmap() used, so we mmap() here with - // read/write permissions and only then mprotect() it to - // no permissions at all. See issue #50313. - let stackptr = get_stack_start_aligned()?; - let result = mmap64( - stackptr, - page_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, - -1, - 0, - ); - if result != stackptr || result == MAP_FAILED { - panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); - } - - let result = mprotect(stackptr, page_size, PROT_NONE); - if result != 0 { - panic!("failed to protect the guard page: {}", io::Error::last_os_error()); - } - - let guardaddr = stackptr.addr(); - - Some(guardaddr..guardaddr + page_size) - } - } - - #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] - pub unsafe fn current() -> Option<Guard> { - let stackptr = get_stack_start()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr) - } - - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "l4re" - ))] - pub unsafe fn current() -> Option<Guard> { - let mut ret = None; - let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); - #[cfg(target_os = "freebsd")] - assert_eq!(libc::pthread_attr_init(&mut attr), 0); - #[cfg(target_os = "freebsd")] - let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); - #[cfg(not(target_os = "freebsd"))] - let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); - if e == 0 { - let mut guardsize = 0; - assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0); - if guardsize == 0 { - if cfg!(all(target_os = "linux", target_env = "musl")) { - // musl versions before 1.1.19 always reported guard - // size obtained from pthread_attr_get_np as zero. - // Use page size as a fallback. - guardsize = PAGE_SIZE.load(Ordering::Relaxed); - } else { - panic!("there is no guard page"); - } - } - let mut stackptr = crate::ptr::null_mut::<libc::c_void>(); - let mut size = 0; - assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackptr, &mut size), 0); - - let stackaddr = stackptr.addr(); - ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { - Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { - Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) - { - // glibc used to include the guard area within the stack, as noted in the BUGS - // section of `man pthread_attr_getguardsize`. This has been corrected starting - // with glibc 2.27, and in some distro backports, so the guard is now placed at the - // end (below) the stack. There's no easy way for us to know which we have at - // runtime, so we'll just match any fault in the range right above or below the - // stack base to call that fault a stack overflow. - Some(stackaddr - guardsize..stackaddr + guardsize) - } else { - Some(stackaddr..stackaddr + guardsize) - }; - } - if e == 0 || cfg!(target_os = "freebsd") { - assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); - } - ret - } -} - // glibc >= 2.15 has a __pthread_get_minstack() function that returns // PTHREAD_STACK_MIN plus bytes needed for thread-local storage. // We need that information to avoid blowing up when a small stack diff --git a/library/std/src/sys/pal/unix/thread_local_dtor.rs b/library/std/src/sys/pal/unix/thread_local_dtor.rs index 79b152cece9..1164c1782f4 100644 --- a/library/std/src/sys/pal/unix/thread_local_dtor.rs +++ b/library/std/src/sys/pal/unix/thread_local_dtor.rs @@ -35,7 +35,7 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { #[cfg(not(sanitizer_cfi_normalize_integers))] #[cfi_encoding = "i"] #[repr(transparent)] - pub struct c_int(pub libc::c_int); + pub struct c_int(#[allow(dead_code)] pub libc::c_int); extern "C" { #[linkage = "extern_weak"] @@ -76,7 +76,13 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { // workaround below is to register, via _tlv_atexit, a custom DTOR list once per // thread. thread_local dtors are pushed to the DTOR list without calling // _tlv_atexit. -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "visionos", + target_os = "tvos" +))] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { use crate::cell::{Cell, RefCell}; use crate::ptr; diff --git a/library/std/src/sys/pal/unix/thread_parking/mod.rs b/library/std/src/sys/pal/unix/thread_parking/mod.rs index 185333c072f..3348d4b366d 100644 --- a/library/std/src/sys/pal/unix/thread_parking/mod.rs +++ b/library/std/src/sys/pal/unix/thread_parking/mod.rs @@ -16,6 +16,7 @@ cfg_if::cfg_if! { target_os = "macos", target_os = "ios", target_os = "watchos", + target_os = "visionos", target_os = "tvos", ), not(miri), diff --git a/library/std/src/sys/pal/unix/thread_parking/pthread.rs b/library/std/src/sys/pal/unix/thread_parking/pthread.rs index bb79cf9548e..d0ad3e2ce3e 100644 --- a/library/std/src/sys/pal/unix/thread_parking/pthread.rs +++ b/library/std/src/sys/pal/unix/thread_parking/pthread.rs @@ -48,6 +48,7 @@ unsafe fn wait_timeout( target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "espidf", target_os = "horizon", ))] @@ -76,6 +77,7 @@ unsafe fn wait_timeout( target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "espidf", target_os = "horizon", )))] @@ -124,6 +126,7 @@ impl Parker { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "l4re", target_os = "android", target_os = "redox", diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index 0440f33ded1..e69775be8cf 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -90,7 +90,8 @@ impl Timespec { target_os = "macos", target_os = "ios", target_os = "tvos", - target_os = "watchos" + target_os = "watchos", + target_os = "visionos", ))] let (tv_sec, tv_nsec) = if (tv_sec <= 0 && tv_sec > i64::MIN) && (tv_nsec < 0 && tv_nsec > -1_000_000_000) { @@ -278,6 +279,7 @@ impl Instant { target_os = "macos", target_os = "ios", target_os = "watchos", + target_os = "visionos", target_os = "tvos" ))] const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW; @@ -285,6 +287,7 @@ impl Instant { target_os = "macos", target_os = "ios", target_os = "watchos", + target_os = "visionos", target_os = "tvos" )))] const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC; diff --git a/library/std/src/sys/pal/unsupported/net.rs b/library/std/src/sys/pal/unsupported/net.rs index 931fe9ba246..87e6106468f 100644 --- a/library/std/src/sys/pal/unsupported/net.rs +++ b/library/std/src/sys/pal/unsupported/net.rs @@ -346,6 +346,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -358,6 +359,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, diff --git a/library/std/src/sys/pal/unsupported/process.rs b/library/std/src/sys/pal/unsupported/process.rs index 6a989dd3e76..2445d9073db 100644 --- a/library/std/src/sys/pal/unsupported/process.rs +++ b/library/std/src/sys/pal/unsupported/process.rs @@ -1,7 +1,6 @@ -use crate::ffi::OsStr; +use crate::ffi::{OsStr, OsString}; use crate::fmt; use crate::io; -use crate::marker::PhantomData; use crate::num::NonZero; use crate::path::Path; use crate::sys::fs::File; @@ -16,7 +15,14 @@ pub use crate::ffi::OsString as EnvKey; //////////////////////////////////////////////////////////////////////////////// pub struct Command { + program: OsString, + args: Vec<OsString>, env: CommandEnv, + + cwd: Option<OsString>, + stdin: Option<Stdio>, + stdout: Option<Stdio>, + stderr: Option<Stdio>, } // passed back to std::process with the pipes connected to the child, if any @@ -27,39 +33,62 @@ pub struct StdioPipes { pub stderr: Option<AnonPipe>, } -// FIXME: This should be a unit struct, so we can always construct it -// The value here should be never used, since we cannot spawn processes. +#[derive(Debug)] pub enum Stdio { Inherit, Null, MakePipe, + ParentStdout, + ParentStderr, + #[allow(dead_code)] // This variant exists only for the Debug impl + InheritFile(File), } impl Command { - pub fn new(_program: &OsStr) -> Command { - Command { env: Default::default() } + pub fn new(program: &OsStr) -> Command { + Command { + program: program.to_owned(), + args: vec![program.to_owned()], + env: Default::default(), + cwd: None, + stdin: None, + stdout: None, + stderr: None, + } } - pub fn arg(&mut self, _arg: &OsStr) {} + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_owned()); + } pub fn env_mut(&mut self) -> &mut CommandEnv { &mut self.env } - pub fn cwd(&mut self, _dir: &OsStr) {} + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.to_owned()); + } - pub fn stdin(&mut self, _stdin: Stdio) {} + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } - pub fn stdout(&mut self, _stdout: Stdio) {} + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } - pub fn stderr(&mut self, _stderr: Stdio) {} + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } pub fn get_program(&self) -> &OsStr { - panic!("unsupported") + &self.program } pub fn get_args(&self) -> CommandArgs<'_> { - CommandArgs { _p: PhantomData } + let mut iter = self.args.iter(); + iter.next(); + CommandArgs { iter } } pub fn get_envs(&self) -> CommandEnvs<'_> { @@ -67,7 +96,7 @@ impl Command { } pub fn get_current_dir(&self) -> Option<&Path> { - None + self.cwd.as_ref().map(|cs| Path::new(cs)) } pub fn spawn( @@ -91,31 +120,83 @@ impl From<AnonPipe> for Stdio { impl From<io::Stdout> for Stdio { fn from(_: io::Stdout) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") + Stdio::ParentStdout } } impl From<io::Stderr> for Stdio { fn from(_: io::Stderr) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") + Stdio::ParentStderr } } impl From<File> for Stdio { - fn from(_file: File) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") + fn from(file: File) -> Stdio { + Stdio::InheritFile(file) } } impl fmt::Debug for Command { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) + // show all attributes + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + let mut debug_command = f.debug_struct("Command"); + debug_command.field("program", &self.program).field("args", &self.args); + if !self.env.is_unchanged() { + debug_command.field("env", &self.env); + } + + if self.cwd.is_some() { + debug_command.field("cwd", &self.cwd); + } + + if self.stdin.is_some() { + debug_command.field("stdin", &self.stdin); + } + if self.stdout.is_some() { + debug_command.field("stdout", &self.stdout); + } + if self.stderr.is_some() { + debug_command.field("stderr", &self.stderr); + } + + debug_command.finish() + } else { + if let Some(ref cwd) = self.cwd { + write!(f, "cd {cwd:?} && ")?; + } + if self.env.does_clear() { + write!(f, "env -i ")?; + // Altered env vars will be printed next, that should exactly work as expected. + } else { + // Removed env vars need the command to be wrapped in `env`. + let mut any_removed = false; + for (key, value_opt) in self.get_envs() { + if value_opt.is_none() { + if !any_removed { + write!(f, "env ")?; + any_removed = true; + } + write!(f, "-u {} ", key.to_string_lossy())?; + } + } + } + // Altered env vars can just be added in front of the program. + for (key, value_opt) in self.get_envs() { + if let Some(value) = value_opt { + write!(f, "{}={value:?} ", key.to_string_lossy())?; + } + } + if self.program != self.args[0] { + write!(f, "[{:?}] ", self.program)?; + } + write!(f, "{:?}", self.args[0])?; + + for arg in &self.args[1..] { + write!(f, " {:?}", arg)?; + } + Ok(()) + } } } @@ -217,23 +298,30 @@ impl Process { } pub struct CommandArgs<'a> { - _p: PhantomData<&'a ()>, + iter: crate::slice::Iter<'a, OsString>, } impl<'a> Iterator for CommandArgs<'a> { type Item = &'a OsStr; fn next(&mut self) -> Option<&'a OsStr> { - None + self.iter.next().map(|os| &**os) } fn size_hint(&self) -> (usize, Option<usize>) { - (0, Some(0)) + self.iter.size_hint() } } -impl<'a> ExactSizeIterator for CommandArgs<'a> {} +impl<'a> ExactSizeIterator for CommandArgs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} impl<'a> fmt::Debug for CommandArgs<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().finish() + f.debug_list().entries(self.iter.clone()).finish() } } diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs index b3a91ee1d4c..ea939247199 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/pal/unsupported/thread.rs @@ -1,5 +1,5 @@ use super::unsupported; -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::time::Duration; @@ -22,10 +22,6 @@ impl Thread { // nope } - pub fn get_name() -> Option<CString> { - None - } - pub fn sleep(_dur: Duration) { panic!("can't sleep"); } @@ -38,13 +34,3 @@ impl Thread { pub fn available_parallelism() -> io::Result<NonZero<usize>> { unsupported() } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option<Guard> { - None - } - pub unsafe fn init() -> Option<Guard> { - None - } -} diff --git a/library/std/src/sys/pal/wasi/net.rs b/library/std/src/sys/pal/wasi/net.rs index 2098d05db0b..b4cf94c8781 100644 --- a/library/std/src/sys/pal/wasi/net.rs +++ b/library/std/src/sys/pal/wasi/net.rs @@ -520,6 +520,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -532,6 +533,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 4b116052f8f..d45fb28b67e 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -1,4 +1,4 @@ -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::mem; use crate::num::NonZero; @@ -134,10 +134,6 @@ impl Thread { // nope } - pub fn get_name() -> Option<CString> { - None - } - pub fn sleep(dur: Duration) { let nanos = dur.as_nanos(); assert!(nanos <= u64::MAX as u128); @@ -193,13 +189,3 @@ impl Thread { pub fn available_parallelism() -> io::Result<NonZero<usize>> { unsupported() } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option<Guard> { - None - } - pub unsafe fn init() -> Option<Guard> { - None - } -} diff --git a/library/std/src/sys/pal/wasip2/cabi_realloc.rs b/library/std/src/sys/pal/wasip2/cabi_realloc.rs new file mode 100644 index 00000000000..820063173d6 --- /dev/null +++ b/library/std/src/sys/pal/wasip2/cabi_realloc.rs @@ -0,0 +1,65 @@ +//! This module contains a canonical definition of the `cabi_realloc` function +//! for the component model. +//! +//! The component model's canonical ABI for representing datatypes in memory +//! makes use of this function when transferring lists and strings, for example. +//! This function behaves like C's `realloc` but also takes alignment into +//! account. +//! +//! Components are notably not required to export this function, but nearly +//! all components end up doing so currently. This definition in the standard +//! library removes the need for all compilations to define this themselves. +//! +//! More information about the canonical ABI can be found at +//! <https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md> +//! +//! Note that the name of this function is not standardized in the canonical ABI +//! at this time. Instead it's a convention of the "componentization process" +//! where a core wasm module is converted to a component to use this name. +//! Additionally this is not the only possible definition of this function, so +//! this is defined as a "weak" symbol. This means that other definitions are +//! allowed to overwrite it if they are present in a compilation. + +use crate::alloc::{self, Layout}; +use crate::ptr; + +#[used] +static FORCE_CODEGEN_OF_CABI_REALLOC: unsafe extern "C" fn( + *mut u8, + usize, + usize, + usize, +) -> *mut u8 = cabi_realloc; + +#[linkage = "weak"] +#[no_mangle] +pub unsafe extern "C" fn cabi_realloc( + old_ptr: *mut u8, + old_len: usize, + align: usize, + new_len: usize, +) -> *mut u8 { + let layout; + let ptr = if old_len == 0 { + if new_len == 0 { + return ptr::without_provenance_mut(align); + } + layout = Layout::from_size_align_unchecked(new_len, align); + alloc::alloc(layout) + } else { + debug_assert_ne!(new_len, 0, "non-zero old_len requires non-zero new_len!"); + layout = Layout::from_size_align_unchecked(old_len, align); + alloc::realloc(old_ptr, layout, new_len) + }; + if ptr.is_null() { + // Print a nice message in debug mode, but in release mode don't + // pull in so many dependencies related to printing so just emit an + // `unreachable` instruction. + if cfg!(debug_assertions) { + alloc::handle_alloc_error(layout); + } else { + super::abort_internal(); + } + } + return ptr; +} diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index d1d444d7b79..94aa458d2f9 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -10,8 +10,6 @@ pub mod alloc; #[path = "../wasi/args.rs"] pub mod args; -#[path = "../unix/cmath.rs"] -pub mod cmath; #[path = "../wasi/env.rs"] pub mod env; #[path = "../wasi/fd.rs"] @@ -28,10 +26,6 @@ pub mod io; pub mod net; #[path = "../wasi/os.rs"] pub mod os; -#[path = "../unix/os_str.rs"] -pub mod os_str; -#[path = "../unix/path.rs"] -pub mod path; #[path = "../unsupported/pipe.rs"] pub mod pipe; #[path = "../unsupported/process.rs"] @@ -72,3 +66,5 @@ pub use helpers::decode_error_kind; use helpers::err2io; pub use helpers::hashmap_random_keys; pub use helpers::is_interrupted; + +mod cabi_realloc; diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs index 3923ff821d9..49f936f1449 100644 --- a/library/std/src/sys/pal/wasm/atomics/thread.rs +++ b/library/std/src/sys/pal/wasm/atomics/thread.rs @@ -1,5 +1,4 @@ use crate::ffi::CStr; -use crate::ffi::CString; use crate::io; use crate::num::NonZero; use crate::sys::unsupported; @@ -18,9 +17,6 @@ impl Thread { pub fn yield_now() {} pub fn set_name(_name: &CStr) {} - pub fn get_name() -> Option<CString> { - None - } pub fn sleep(dur: Duration) { use crate::arch::wasm32; diff --git a/library/std/src/sys/pal/windows/args.rs b/library/std/src/sys/pal/windows/args.rs index 2ecfe088d10..5098c05196e 100644 --- a/library/std/src/sys/pal/windows/args.rs +++ b/library/std/src/sys/pal/windows/args.rs @@ -7,7 +7,7 @@ mod tests; use super::os::current_exe; -use crate::ffi::OsString; +use crate::ffi::{OsStr, OsString}; use crate::fmt; use crate::io; use crate::num::NonZero; @@ -17,6 +17,7 @@ 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::vec; use crate::iter; @@ -262,16 +263,92 @@ pub(crate) fn append_arg(cmd: &mut Vec<u16>, arg: &Arg, force_quotes: bool) -> i Ok(()) } +fn append_bat_arg(cmd: &mut Vec<u16>, arg: &OsStr, mut quote: bool) -> io::Result<()> { + ensure_no_nuls(arg)?; + // If an argument has 0 characters then we need to quote it to ensure + // that it actually gets passed through on the command line or otherwise + // it will be dropped entirely when parsed on the other end. + // + // We also need to quote the argument if it ends with `\` to guard against + // bat usage such as `"%~2"` (i.e. force quote arguments) otherwise a + // trailing slash will escape the closing quote. + if arg.is_empty() || arg.as_encoded_bytes().last() == Some(&b'\\') { + quote = true; + } + for cp in arg.as_inner().inner.code_points() { + if let Some(cp) = cp.to_char() { + // Rather than trying to find every ascii symbol that must be quoted, + // we assume that all ascii symbols must be quoted unless they're known to be good. + // We also quote Unicode control blocks for good measure. + // Note an unquoted `\` is fine so long as the argument isn't otherwise quoted. + static UNQUOTED: &str = r"#$*+-./:?@\_"; + let ascii_needs_quotes = + cp.is_ascii() && !(cp.is_ascii_alphanumeric() || UNQUOTED.contains(cp)); + if ascii_needs_quotes || cp.is_control() { + quote = true; + } + } + } + + if quote { + cmd.push('"' as u16); + } + // Loop through the string, escaping `\` only if followed by `"`. + // And escaping `"` by doubling them. + let mut backslashes: usize = 0; + for x in arg.encode_wide() { + if x == '\\' as u16 { + backslashes += 1; + } else { + if x == '"' as u16 { + // Add n backslashes to total 2n before internal `"`. + cmd.extend((0..backslashes).map(|_| '\\' as u16)); + // Appending an additional double-quote acts as an escape. + cmd.push(b'"' as u16) + } else if x == '%' as u16 || x == '\r' as u16 { + // yt-dlp hack: replaces `%` with `%%cd:~,%` to stop %VAR% being expanded as an environment variable. + // + // # Explanation + // + // cmd supports extracting a substring from a variable using the following syntax: + // %variable:~start_index,end_index% + // + // In the above command `cd` is used as the variable and the start_index and end_index are left blank. + // `cd` is a built-in variable that dynamically expands to the current directory so it's always available. + // Explicitly omitting both the start and end index creates a zero-length substring. + // + // Therefore it all resolves to nothing. However, by doing this no-op we distract cmd.exe + // from potentially expanding %variables% in the argument. + cmd.extend_from_slice(&[ + '%' as u16, '%' as u16, 'c' as u16, 'd' as u16, ':' as u16, '~' as u16, + ',' as u16, + ]); + } + backslashes = 0; + } + cmd.push(x); + } + if quote { + // Add n backslashes to total 2n before ending `"`. + cmd.extend((0..backslashes).map(|_| '\\' as u16)); + cmd.push('"' as u16); + } + Ok(()) +} + pub(crate) fn make_bat_command_line( script: &[u16], args: &[Arg], force_quotes: bool, ) -> io::Result<Vec<u16>> { + const INVALID_ARGUMENT_ERROR: io::Error = + io::const_io_error!(io::ErrorKind::InvalidInput, r#"batch file arguments are invalid"#); // Set the start of the command line to `cmd.exe /c "` // It is necessary to surround the command in an extra pair of quotes, // hence the trailing quote here. It will be closed after all arguments // have been added. - let mut cmd: Vec<u16> = "cmd.exe /d /c \"".encode_utf16().collect(); + // Using /e:ON enables "command extensions" which is essential for the `%` hack to work. + let mut cmd: Vec<u16> = "cmd.exe /e:ON /v:OFF /d /c \"".encode_utf16().collect(); // Push the script name surrounded by its quote pair. cmd.push(b'"' as u16); @@ -291,18 +368,22 @@ pub(crate) fn make_bat_command_line( // reconstructed by the batch script by default. for arg in args { cmd.push(' ' as u16); - // Make sure to always quote special command prompt characters, including: - // * Characters `cmd /?` says require quotes. - // * `%` for environment variables, as in `%TMP%`. - // * `|<>` pipe/redirect characters. - const SPECIAL: &[u8] = b"\t &()[]{}^=;!'+,`~%|<>"; - let force_quotes = match arg { - Arg::Regular(arg) if !force_quotes => { - arg.as_encoded_bytes().iter().any(|c| SPECIAL.contains(c)) + match arg { + Arg::Regular(arg_os) => { + let arg_bytes = arg_os.as_encoded_bytes(); + // Disallow \r and \n as they may truncate the arguments. + const DISALLOWED: &[u8] = b"\r\n"; + if arg_bytes.iter().any(|c| DISALLOWED.contains(c)) { + return Err(INVALID_ARGUMENT_ERROR); + } + append_bat_arg(&mut cmd, arg_os, force_quotes)?; + } + _ => { + // Raw arguments are passed on as-is. + // It's the user's responsibility to properly handle arguments in this case. + append_arg(&mut cmd, arg, force_quotes)?; } - _ => force_quotes, }; - append_arg(&mut cmd, arg, force_quotes)?; } // Close the quote we left opened earlier. diff --git a/library/std/src/sys/pal/windows/stack_overflow.rs b/library/std/src/sys/pal/windows/stack_overflow.rs index 627763da856..f93f31026f8 100644 --- a/library/std/src/sys/pal/windows/stack_overflow.rs +++ b/library/std/src/sys/pal/windows/stack_overflow.rs @@ -3,21 +3,12 @@ use crate::sys::c; use crate::thread; -use super::api; - -pub struct Handler; - -impl Handler { - pub unsafe fn new() -> Handler { - // This API isn't available on XP, so don't panic in that case and just - // pray it works out ok. - if c::SetThreadStackGuarantee(&mut 0x5000) == 0 - && api::get_last_error().code != c::ERROR_CALL_NOT_IMPLEMENTED - { - panic!("failed to reserve stack space for exception handling"); - } - Handler - } +/// Reserve stack space for use in stack overflow exceptions. +pub unsafe fn reserve_stack() { + let result = c::SetThreadStackGuarantee(&mut 0x5000); + // Reserving stack space is not critical so we allow it to fail in the released build of libstd. + // We still use debug assert here so that CI will test that we haven't made a mistake calling the function. + debug_assert_ne!(result, 0, "failed to reserve stack space for exception handling"); } unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> c::LONG { @@ -36,9 +27,10 @@ unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POIN } pub unsafe fn init() { - if c::AddVectoredExceptionHandler(0, Some(vectored_handler)).is_null() { - panic!("failed to install exception handler"); - } + let result = c::AddVectoredExceptionHandler(0, Some(vectored_handler)); + // Similar to the above, adding the stack overflow handler is allowed to fail + // but a debug assert is used so CI will still test that it normally works. + debug_assert!(!result.is_null(), "failed to install exception handler"); // Set the thread stack guarantee for the main thread. - let _h = Handler::new(); + reserve_stack(); } diff --git a/library/std/src/sys/pal/windows/stack_overflow_uwp.rs b/library/std/src/sys/pal/windows/stack_overflow_uwp.rs index afdf7f566ae..9e9b3efaf1b 100644 --- a/library/std/src/sys/pal/windows/stack_overflow_uwp.rs +++ b/library/std/src/sys/pal/windows/stack_overflow_uwp.rs @@ -1,11 +1,4 @@ #![cfg_attr(test, allow(dead_code))] -pub struct Handler; - -impl Handler { - pub fn new() -> Handler { - Handler - } -} - +pub unsafe fn reserve_stack() {} pub unsafe fn init() {} diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index 970bd9c6ce7..c0c63c3340f 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -9,7 +9,6 @@ use crate::sys::handle::Handle; use crate::sys::stack_overflow; use crate::sys_common::FromInner; use crate::time::Duration; -use alloc::ffi::CString; use core::ffi::c_void; use super::time::WaitableTimer; @@ -48,9 +47,8 @@ impl Thread { extern "system" fn thread_start(main: *mut c_void) -> c::DWORD { unsafe { - // Next, set up our stack overflow handler which may get triggered if we run - // out of stack. - let _handler = stack_overflow::Handler::new(); + // Next, reserve some stack space for if we otherwise run out of stack. + stack_overflow::reserve_stack(); // Finally, let's run some code. Box::from_raw(main as *mut Box<dyn FnOnce()>)(); } @@ -68,29 +66,6 @@ impl Thread { }; } - pub fn get_name() -> Option<CString> { - unsafe { - let mut ptr = core::ptr::null_mut(); - let result = c::GetThreadDescription(c::GetCurrentThread(), &mut ptr); - if result < 0 { - return None; - } - let name = String::from_utf16_lossy({ - let mut len = 0; - while *ptr.add(len) != 0 { - len += 1; - } - core::slice::from_raw_parts(ptr, len) - }) - .into_bytes(); - // Attempt to free the memory. - // This should never fail but if it does then there's not much we can do about it. - let result = c::LocalFree(ptr.cast::<c_void>()); - debug_assert!(result.is_null()); - if name.is_empty() { None } else { Some(CString::from_vec_unchecked(name)) } - } - } - pub fn join(self) { let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; if rc == c::WAIT_FAILED { @@ -144,14 +119,3 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { cpus => Ok(unsafe { NonZero::new_unchecked(cpus) }), } } - -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option<Guard> { - None - } - pub unsafe fn init() -> Option<Guard> { - None - } -} diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs index f95ceb7343b..da7d722cc70 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/pal/xous/thread.rs @@ -1,4 +1,4 @@ -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::os::xous::ffi::{ @@ -113,10 +113,6 @@ impl Thread { // nope } - pub fn get_name() -> Option<CString> { - None - } - pub fn sleep(dur: Duration) { // Because the sleep server works on units of `usized milliseconds`, split // the messages up into these chunks. This means we may run into issues @@ -140,13 +136,3 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { // We're unicore right now. Ok(unsafe { NonZero::new_unchecked(1) }) } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option<Guard> { - None - } - pub unsafe fn init() -> Option<Guard> { - None - } -} diff --git a/library/std/src/sys/pal/xous/thread_local_key.rs b/library/std/src/sys/pal/xous/thread_local_key.rs index 2aaf46d0244..6c29813c79d 100644 --- a/library/std/src/sys/pal/xous/thread_local_key.rs +++ b/library/std/src/sys/pal/xous/thread_local_key.rs @@ -49,7 +49,7 @@ fn tls_ptr_addr() -> *mut *mut u8 { out(reg) tp, ); } - core::ptr::from_exposed_addr_mut::<*mut u8>(tp) + core::ptr::with_exposed_provenance_mut::<*mut u8>(tp) } /// Create an area of memory that's unique per thread. This area will diff --git a/library/std/src/sys/pal/zkvm/thread_local_key.rs b/library/std/src/sys/pal/zkvm/thread_local_key.rs index 3ffe6247344..2f67924c618 100644 --- a/library/std/src/sys/pal/zkvm/thread_local_key.rs +++ b/library/std/src/sys/pal/zkvm/thread_local_key.rs @@ -9,13 +9,13 @@ pub unsafe fn create(_dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key { #[inline] pub unsafe fn set(key: Key, value: *mut u8) { - let key: *mut *mut u8 = core::ptr::from_exposed_addr_mut(key); + let key: *mut *mut u8 = core::ptr::with_exposed_provenance_mut(key); *key = value; } #[inline] pub unsafe fn get(key: Key) -> *mut u8 { - let key: *mut *mut u8 = core::ptr::from_exposed_addr_mut(key); + let key: *mut *mut u8 = core::ptr::with_exposed_provenance_mut(key); *key } diff --git a/library/std/src/sys/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs index a78084de0fa..3f3615ea3e0 100644 --- a/library/std/src/sys/personality/dwarf/eh.rs +++ b/library/std/src/sys/personality/dwarf/eh.rs @@ -125,7 +125,7 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result // Can never have null landing pad for sjlj -- that would have // been indicated by a -1 call site index. // FIXME(strict provenance) - let lpad = ptr::from_exposed_addr((cs_lpad + 1) as usize); + let lpad = ptr::with_exposed_provenance((cs_lpad + 1) as usize); return Ok(interpret_cs_action(action_table, cs_action_entry, lpad)); } } diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 6f317131145..b0f744dd966 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -92,7 +92,7 @@ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c cfg_if::cfg_if! { - if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "netbsd")))] { + if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "visionos"), not(target_os = "netbsd")))] { // ARM EHABI personality routine. // https://web.archive.org/web/20190728160938/https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf // diff --git a/library/std/src/sys/sync/condvar/pthread.rs b/library/std/src/sys/sync/condvar/pthread.rs index 728371685ee..0475f985078 100644 --- a/library/std/src/sys/sync/condvar/pthread.rs +++ b/library/std/src/sys/sync/condvar/pthread.rs @@ -34,6 +34,7 @@ impl LazyInit for AllocatedCondvar { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "l4re", target_os = "android", target_os = "redox" @@ -127,6 +128,7 @@ impl Condvar { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "android", target_os = "espidf", target_os = "horizon" @@ -162,6 +164,7 @@ impl Condvar { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "visionos", target_os = "android", target_os = "espidf", target_os = "horizon" diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs index dce966086b8..d1918855797 100644 --- a/library/std/src/sys/sync/rwlock/queue.rs +++ b/library/std/src/sys/sync/rwlock/queue.rs @@ -115,8 +115,7 @@ use crate::sync::atomic::{ AtomicBool, AtomicPtr, Ordering::{AcqRel, Acquire, Relaxed, Release}, }; -use crate::sys_common::thread_info; -use crate::thread::Thread; +use crate::thread::{self, Thread}; // Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the // locking operation will be retried. @@ -203,8 +202,7 @@ impl Node { fn prepare(&mut self) { // Fall back to creating an unnamed `Thread` handle to allow locking in // TLS destructors. - self.thread - .get_or_init(|| thread_info::current_thread().unwrap_or_else(|| Thread::new(None))); + self.thread.get_or_init(|| thread::try_current().unwrap_or_else(|| Thread::new(None))); self.completed = AtomicBool::new(false); } diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 5410f135a73..5abf201aa20 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -26,7 +26,6 @@ pub mod io; pub mod lazy_box; pub mod process; pub mod thread; -pub mod thread_info; pub mod thread_local_dtor; pub mod thread_parking; pub mod wstr; diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 581c46af0ea..8c9885974b4 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -18,7 +18,7 @@ use crate::ffi::{c_int, c_void}; cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", target_os = "tvos", target_os = "macos", target_os = "watchos", + target_os = "ios", target_os = "tvos", target_os = "macos", target_os = "watchos", target_os = "visionos", target_os = "openbsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris", target_os = "haiku", target_os = "l4re", target_os = "nto"))] { use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; @@ -107,13 +107,13 @@ where pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, len: usize) -> io::Result<SocketAddr> { match storage.ss_family as c_int { c::AF_INET => { - assert!(len as usize >= mem::size_of::<c::sockaddr_in>()); + assert!(len >= mem::size_of::<c::sockaddr_in>()); Ok(SocketAddr::V4(FromInner::from_inner(unsafe { *(storage as *const _ as *const c::sockaddr_in) }))) } c::AF_INET6 => { - assert!(len as usize >= mem::size_of::<c::sockaddr_in6>()); + assert!(len >= mem::size_of::<c::sockaddr_in6>()); Ok(SocketAddr::V6(FromInner::from_inner(unsafe { *(storage as *const _ as *const c::sockaddr_in6) }))) diff --git a/library/std/src/sys_common/thread_info.rs b/library/std/src/sys_common/thread_info.rs deleted file mode 100644 index ec1428ea40e..00000000000 --- a/library/std/src/sys_common/thread_info.rs +++ /dev/null @@ -1,53 +0,0 @@ -#![allow(dead_code)] // stack_guard isn't used right now on all platforms - -use crate::cell::OnceCell; -use crate::sys; -use crate::sys::thread::guard::Guard; -use crate::thread::Thread; - -struct ThreadInfo { - stack_guard: OnceCell<Guard>, - thread: OnceCell<Thread>, -} - -thread_local! { - static THREAD_INFO: ThreadInfo = const { ThreadInfo { - stack_guard: OnceCell::new(), - thread: OnceCell::new() - } }; -} - -impl ThreadInfo { - fn with<R, F>(f: F) -> Option<R> - where - F: FnOnce(&Thread, &OnceCell<Guard>) -> R, - { - THREAD_INFO - .try_with(move |thread_info| { - let thread = - thread_info.thread.get_or_init(|| Thread::new(sys::thread::Thread::get_name())); - f(thread, &thread_info.stack_guard) - }) - .ok() - } -} - -pub fn current_thread() -> Option<Thread> { - ThreadInfo::with(|thread, _| thread.clone()) -} - -pub fn stack_guard() -> Option<Guard> { - ThreadInfo::with(|_, guard| guard.get().cloned()).flatten() -} - -/// Set new thread info, panicking if it has already been initialized -#[allow(unreachable_code, unreachable_patterns)] // some platforms don't use stack_guard -pub fn set(stack_guard: Option<Guard>, thread: Thread) { - THREAD_INFO.with(move |thread_info| { - rtassert!(thread_info.stack_guard.get().is_none() && thread_info.thread.get().is_none()); - if let Some(guard) = stack_guard { - thread_info.stack_guard.set(guard).unwrap(); - } - thread_info.thread.set(thread).unwrap(); - }); -} diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs index 7dcc1141099..a9cd26389cd 100644 --- a/library/std/src/sys_common/thread_local_key.rs +++ b/library/std/src/sys_common/thread_local_key.rs @@ -1,4 +1,4 @@ -//! OS-based thread local storage +//! OS-based thread local storage for non-Windows systems //! //! This module provides an implementation of OS-based thread local storage, //! using the native OS-provided facilities (think `TlsAlloc` or @@ -11,6 +11,9 @@ //! the OS-TLS key. The other is a type which does implement `Drop` and hence //! has a safe interface. //! +//! Windows doesn't use this module at all; `sys::pal::windows::thread_local_key` +//! gets imported in its stead. +//! //! # Usage //! //! This module should likely not be used directly unless other primitives are diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 85de2980133..f792a27dd69 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -159,7 +159,7 @@ mod tests; use crate::any::Any; -use crate::cell::UnsafeCell; +use crate::cell::{OnceCell, UnsafeCell}; use crate::ffi::{CStr, CString}; use crate::fmt; use crate::io; @@ -174,7 +174,6 @@ use crate::str; use crate::sync::Arc; use crate::sys::thread as imp; use crate::sys_common::thread; -use crate::sys_common::thread_info; use crate::sys_common::thread_parking::Parker; use crate::sys_common::{AsInner, IntoInner}; use crate::time::{Duration, Instant}; @@ -518,12 +517,8 @@ impl Builder { crate::io::set_output_capture(output_capture); - // SAFETY: we constructed `f` initialized. let f = f.into_inner(); - // SAFETY: the stack guard passed is the one for the current thread. - // This means the current thread's stack and the new thread's stack - // are properly set and protected from each other. - thread_info::set(unsafe { imp::guard::current() }, their_thread); + set_current(their_thread); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { crate::sys_common::backtrace::__rust_begin_short_backtrace(f) })); @@ -683,6 +678,25 @@ where Builder::new().spawn(f).expect("failed to spawn thread") } +thread_local! { + static CURRENT: OnceCell<Thread> = const { OnceCell::new() }; +} + +/// Sets the thread handle for the current thread. +/// +/// Panics if the handle has been set already or when called from a TLS destructor. +pub(crate) fn set_current(thread: Thread) { + CURRENT.with(|current| current.set(thread).unwrap()); +} + +/// Gets a handle to the thread that invokes it. +/// +/// In contrast to the public `current` function, this will not panic if called +/// from inside a TLS destructor. +pub(crate) fn try_current() -> Option<Thread> { + CURRENT.try_with(|current| current.get_or_init(|| Thread::new(None)).clone()).ok() +} + /// Gets a handle to the thread that invokes it. /// /// # Examples @@ -705,7 +719,7 @@ where #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub fn current() -> Thread { - thread_info::current_thread().expect( + try_current().expect( "use of std::thread::current() is not possible \ after the thread's local data has been destroyed", ) @@ -1231,9 +1245,16 @@ impl ThreadId { // Thread //////////////////////////////////////////////////////////////////////////////// +/// The internal representation of a `Thread`'s name. +enum ThreadName { + Main, + Other(CString), + Unnamed, +} + /// The internal representation of a `Thread` handle struct Inner { - name: Option<CString>, // Guaranteed to be UTF-8 + name: ThreadName, // Guaranteed to be UTF-8 id: ThreadId, parker: Parker, } @@ -1270,8 +1291,20 @@ pub struct Thread { impl Thread { // Used only internally to construct a thread object without spawning - // Panics if the name contains nuls. pub(crate) fn new(name: Option<CString>) -> Thread { + if let Some(name) = name { + Self::new_inner(ThreadName::Other(name)) + } else { + Self::new_inner(ThreadName::Unnamed) + } + } + + // Used in runtime to construct main thread + pub(crate) fn new_main() -> Thread { + Self::new_inner(ThreadName::Main) + } + + fn new_inner(name: ThreadName) -> Thread { // We have to use `unsafe` here to construct the `Parker` in-place, // which is required for the UNIX implementation. // @@ -1398,7 +1431,11 @@ impl Thread { } fn cname(&self) -> Option<&CStr> { - self.inner.name.as_deref() + match &self.inner.name { + ThreadName::Main => Some(c"main"), + ThreadName::Other(other) => Some(&other), + ThreadName::Unnamed => None, + } } } diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index 589a5fdad1d..494513f2c75 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -43,7 +43,8 @@ fn test_named_thread() { target_os = "macos", target_os = "ios", target_os = "tvos", - target_os = "watchos" + target_os = "watchos", + target_os = "visionos", ))] #[test] fn test_named_thread_truncation() { @@ -69,26 +70,6 @@ fn test_named_thread_truncation() { result.unwrap().join().unwrap(); } -#[cfg(any( - all(target_os = "windows", not(target_vendor = "win7")), - target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos" -))] -#[test] -fn test_get_os_named_thread() { - use crate::sys::thread::Thread; - // Spawn a new thread to avoid interfering with other tests running on this thread. - let handler = thread::spawn(|| { - let name = c"test me please"; - Thread::set_name(name); - assert_eq!(name, Thread::get_name().unwrap().as_c_str()); - }); - handler.join().unwrap(); -} - #[test] #[should_panic] fn test_invalid_named_thread() { |
