diff options
| author | Laurențiu Nicola <lnicola@dend.ro> | 2025-06-09 15:44:40 +0300 |
|---|---|---|
| committer | Laurențiu Nicola <lnicola@dend.ro> | 2025-06-09 15:44:40 +0300 |
| commit | 88223c56d9352a14bf4e91d706d68ca3a696bcdf (patch) | |
| tree | 1fa465adaaf07355079312d2e1aa3e8594acadc7 /library/std/src/sys | |
| parent | cbe6fe86ef60ceedd46128df8f09da982f44191a (diff) | |
| parent | 7c10378e1fee5ddc6573b916aeb884ab10e0de17 (diff) | |
| download | rust-88223c56d9352a14bf4e91d706d68ca3a696bcdf.tar.gz rust-88223c56d9352a14bf4e91d706d68ca3a696bcdf.zip | |
Merge from rust-lang/rust
Diffstat (limited to 'library/std/src/sys')
29 files changed, 473 insertions, 178 deletions
diff --git a/library/std/src/sys/alloc/mod.rs b/library/std/src/sys/alloc/mod.rs index 8489e17c971..f3af1f7f599 100644 --- a/library/std/src/sys/alloc/mod.rs +++ b/library/std/src/sys/alloc/mod.rs @@ -17,6 +17,7 @@ const MIN_ALIGN: usize = if cfg!(any( target_arch = "arm", target_arch = "m68k", target_arch = "csky", + target_arch = "loongarch32", target_arch = "mips", target_arch = "mips32r6", target_arch = "powerpc", diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs index a9774bef9e3..175d919c289 100644 --- a/library/std/src/sys/fs/hermit.rs +++ b/library/std/src/sys/fs/hermit.rs @@ -422,6 +422,10 @@ impl File { self.0.seek(pos) } + pub fn size(&self) -> Option<io::Result<u64>> { + None + } + pub fn tell(&self) -> io::Result<u64> { self.0.tell() } diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index 3bfb39bac95..808a9582911 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -459,6 +459,10 @@ impl File { self.tell() } + pub fn size(&self) -> Option<io::Result<u64>> { + None + } + pub fn tell(&self) -> io::Result<u64> { unsafe { let mut out_offset = MaybeUninit::uninit(); diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index 416c90b98b6..5763d7862f5 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -280,6 +280,10 @@ impl File { self.0 } + pub fn size(&self) -> Option<io::Result<u64>> { + self.0 + } + pub fn tell(&self) -> io::Result<u64> { self.0 } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index a3e520fdeef..dc278274f00 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1464,6 +1464,15 @@ impl File { Ok(n as u64) } + pub fn size(&self) -> Option<io::Result<u64>> { + match self.file_attr().map(|attr| attr.size()) { + // Fall back to default implementation if the returned size is 0, + // we might be in a proc mount. + Ok(0) => None, + result => Some(result), + } + } + pub fn tell(&self) -> io::Result<u64> { self.seek(SeekFrom::Current(0)) } diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 0ff9533c047..efaddb51b37 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -259,6 +259,10 @@ impl File { self.0 } + pub fn size(&self) -> Option<io::Result<u64>> { + self.0 + } + pub fn tell(&self) -> io::Result<u64> { self.0 } diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs index ebfc7377a2e..b65d86de12a 100644 --- a/library/std/src/sys/fs/wasi.rs +++ b/library/std/src/sys/fs/wasi.rs @@ -516,6 +516,10 @@ impl File { self.fd.seek(pos) } + pub fn size(&self) -> Option<io::Result<u64>> { + None + } + pub fn tell(&self) -> io::Result<u64> { self.fd.tell() } diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index 9039fd00f5d..a95709b4891 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -415,10 +415,7 @@ impl File { match result { Ok(_) => Ok(()), - Err(err) - if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) - || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => - { + Err(err) if err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => { Err(TryLockError::WouldBlock) } Err(err) => Err(TryLockError::Error(err)), @@ -440,10 +437,7 @@ impl File { match result { Ok(_) => Ok(()), - Err(err) - if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) - || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => - { + Err(err) if err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => { Err(TryLockError::WouldBlock) } Err(err) => Err(TryLockError::Error(err)), @@ -622,6 +616,14 @@ impl File { Ok(newpos as u64) } + pub fn size(&self) -> Option<io::Result<u64>> { + let mut result = 0; + Some( + cvt(unsafe { c::GetFileSizeEx(self.handle.as_raw_handle(), &mut result) }) + .map(|_| result as u64), + ) + } + pub fn tell(&self) -> io::Result<u64> { self.seek(SeekFrom::Current(0)) } diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index da217439626..46d67c8e510 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -4,11 +4,14 @@ use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::sys::unsupported; use crate::time::Duration; -pub struct TcpStream(!); +mod tcp; +pub(crate) mod tcp4; + +pub struct TcpStream(#[expect(dead_code)] tcp::Tcp); impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> { - unsupported() + pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> { + tcp::Tcp::connect(addr?).map(Self) } pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> { @@ -16,105 +19,105 @@ impl TcpStream { } pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { - self.0 + unsupported() } pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { - self.0 + unsupported() } pub fn read_timeout(&self) -> io::Result<Option<Duration>> { - self.0 + unsupported() } pub fn write_timeout(&self) -> io::Result<Option<Duration>> { - self.0 + unsupported() } pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { - self.0 + unsupported() } pub fn read(&self, _: &mut [u8]) -> io::Result<usize> { - self.0 + unsupported() } pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0 + unsupported() } pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - self.0 + unsupported() } pub fn is_read_vectored(&self) -> bool { - self.0 + false } pub fn write(&self, _: &[u8]) -> io::Result<usize> { - self.0 + unsupported() } pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> { - self.0 + unsupported() } pub fn is_write_vectored(&self) -> bool { - self.0 + false } pub fn peer_addr(&self) -> io::Result<SocketAddr> { - self.0 + unsupported() } pub fn socket_addr(&self) -> io::Result<SocketAddr> { - self.0 + unsupported() } pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - self.0 + unsupported() } pub fn duplicate(&self) -> io::Result<TcpStream> { - self.0 + unsupported() } pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> { - self.0 + unsupported() } pub fn linger(&self) -> io::Result<Option<Duration>> { - self.0 + unsupported() } pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - self.0 + unsupported() } pub fn nodelay(&self) -> io::Result<bool> { - self.0 + unsupported() } pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 + unsupported() } pub fn ttl(&self) -> io::Result<u32> { - self.0 + unsupported() } pub fn take_error(&self) -> io::Result<Option<io::Error>> { - self.0 + unsupported() } pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 + unsupported() } } impl fmt::Debug for TcpStream { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + todo!() } } diff --git a/library/std/src/sys/net/connection/uefi/tcp.rs b/library/std/src/sys/net/connection/uefi/tcp.rs new file mode 100644 index 00000000000..f87accdc41d --- /dev/null +++ b/library/std/src/sys/net/connection/uefi/tcp.rs @@ -0,0 +1,21 @@ +use super::tcp4; +use crate::io; +use crate::net::SocketAddr; + +pub(crate) enum Tcp { + V4(#[expect(dead_code)] tcp4::Tcp4), +} + +impl Tcp { + pub(crate) fn connect(addr: &SocketAddr) -> io::Result<Self> { + match addr { + SocketAddr::V4(x) => { + let temp = tcp4::Tcp4::new()?; + temp.configure(true, Some(x), None)?; + temp.connect()?; + Ok(Tcp::V4(temp)) + } + SocketAddr::V6(_) => todo!(), + } + } +} diff --git a/library/std/src/sys/net/connection/uefi/tcp4.rs b/library/std/src/sys/net/connection/uefi/tcp4.rs new file mode 100644 index 00000000000..f7ca373b52b --- /dev/null +++ b/library/std/src/sys/net/connection/uefi/tcp4.rs @@ -0,0 +1,118 @@ +use r_efi::efi::{self, Status}; +use r_efi::protocols::tcp4; + +use crate::io; +use crate::net::SocketAddrV4; +use crate::ptr::NonNull; +use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sys::pal::helpers; + +const TYPE_OF_SERVICE: u8 = 8; +const TIME_TO_LIVE: u8 = 255; + +pub(crate) struct Tcp4 { + protocol: NonNull<tcp4::Protocol>, + flag: AtomicBool, + #[expect(dead_code)] + service_binding: helpers::ServiceProtocol, +} + +const DEFAULT_ADDR: efi::Ipv4Address = efi::Ipv4Address { addr: [0u8; 4] }; + +impl Tcp4 { + pub(crate) fn new() -> io::Result<Self> { + let service_binding = helpers::ServiceProtocol::open(tcp4::SERVICE_BINDING_PROTOCOL_GUID)?; + let protocol = helpers::open_protocol(service_binding.child_handle(), tcp4::PROTOCOL_GUID)?; + + Ok(Self { service_binding, protocol, flag: AtomicBool::new(false) }) + } + + pub(crate) fn configure( + &self, + active: bool, + remote_address: Option<&SocketAddrV4>, + station_address: Option<&SocketAddrV4>, + ) -> io::Result<()> { + let protocol = self.protocol.as_ptr(); + + let (remote_address, remote_port) = if let Some(x) = remote_address { + (helpers::ipv4_to_r_efi(*x.ip()), x.port()) + } else { + (DEFAULT_ADDR, 0) + }; + + // FIXME: Remove when passive connections with proper subnet handling are added + assert!(station_address.is_none()); + let use_default_address = efi::Boolean::TRUE; + let (station_address, station_port) = (DEFAULT_ADDR, 0); + let subnet_mask = helpers::ipv4_to_r_efi(crate::net::Ipv4Addr::new(0, 0, 0, 0)); + + let mut config_data = tcp4::ConfigData { + type_of_service: TYPE_OF_SERVICE, + time_to_live: TIME_TO_LIVE, + access_point: tcp4::AccessPoint { + use_default_address, + remote_address, + remote_port, + active_flag: active.into(), + station_address, + station_port, + subnet_mask, + }, + control_option: crate::ptr::null_mut(), + }; + + let r = unsafe { ((*protocol).configure)(protocol, &mut config_data) }; + if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } + + pub(crate) fn connect(&self) -> io::Result<()> { + let evt = unsafe { self.create_evt() }?; + let completion_token = + tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS }; + + let protocol = self.protocol.as_ptr(); + let mut conn_token = tcp4::ConnectionToken { completion_token }; + + let r = unsafe { ((*protocol).connect)(protocol, &mut conn_token) }; + if r.is_error() { + return Err(io::Error::from_raw_os_error(r.as_usize())); + } + + self.wait_for_flag(); + + if completion_token.status.is_error() { + Err(io::Error::from_raw_os_error(completion_token.status.as_usize())) + } else { + Ok(()) + } + } + + unsafe fn create_evt(&self) -> io::Result<helpers::OwnedEvent> { + self.flag.store(false, Ordering::Relaxed); + helpers::OwnedEvent::new( + efi::EVT_NOTIFY_SIGNAL, + efi::TPL_CALLBACK, + Some(toggle_atomic_flag), + Some(unsafe { NonNull::new_unchecked(self.flag.as_ptr().cast()) }), + ) + } + + fn wait_for_flag(&self) { + while !self.flag.load(Ordering::Relaxed) { + let _ = self.poll(); + } + } + + fn poll(&self) -> io::Result<()> { + let protocol = self.protocol.as_ptr(); + let r = unsafe { ((*protocol).poll)(protocol) }; + + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } +} + +extern "efiapi" fn toggle_atomic_flag(_: r_efi::efi::Event, ctx: *mut crate::ffi::c_void) { + let flag = unsafe { AtomicBool::from_ptr(ctx.cast()) }; + flag.store(true, Ordering::Relaxed); +} diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index 4a8808c9230..f8ab4543a3a 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -115,7 +115,7 @@ impl Buf { } #[inline] - pub fn from_string(s: String) -> Buf { + pub const fn from_string(s: String) -> Buf { Buf { inner: s.into_bytes() } } diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index 5174ea65d0c..bbc704ebf86 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -92,7 +92,7 @@ impl Buf { } #[inline] - pub fn from_string(s: String) -> Buf { + pub const fn from_string(s: String) -> Buf { Buf { inner: Wtf8Buf::from_string(s) } } @@ -215,9 +215,9 @@ impl Buf { /// The slice must be valid for the platform encoding (as described in /// [`Slice::from_encoded_bytes_unchecked`]). /// - /// This bypasses the WTF-8 surrogate joining, so `self` must not end with a - /// leading surrogate half and `other` must not start with with a trailing - /// surrogate half. + /// This bypasses the WTF-8 surrogate joining, so either `self` must not + /// end with a leading surrogate half, or `other` must not start with a + /// trailing surrogate half. #[inline] pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { self.inner.extend_from_slice(other); diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 6ee3e0a8b66..e47263348db 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -653,7 +653,6 @@ pub(crate) struct ServiceProtocol { } impl ServiceProtocol { - #[expect(dead_code)] pub(crate) fn open(service_guid: r_efi::efi::Guid) -> io::Result<Self> { let handles = locate_handles(service_guid)?; @@ -670,7 +669,6 @@ impl ServiceProtocol { Err(io::const_error!(io::ErrorKind::NotFound, "no service binding protocol found")) } - #[expect(dead_code)] pub(crate) fn child_handle(&self) -> NonNull<crate::ffi::c_void> { self.child_handle } @@ -732,6 +730,10 @@ impl OwnedEvent { } } + pub(crate) fn as_ptr(&self) -> efi::Event { + self.0.as_ptr() + } + pub(crate) fn into_raw(self) -> *mut crate::ffi::c_void { let r = self.0.as_ptr(); crate::mem::forget(self); @@ -755,3 +757,7 @@ impl Drop for OwnedEvent { } } } + +pub(crate) const fn ipv4_to_r_efi(addr: crate::net::Ipv4Addr) -> efi::Ipv4Address { + efi::Ipv4Address { addr: addr.octets() } +} diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 48609030aed..850bdfdf5b5 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -282,7 +282,7 @@ pub fn current_exe() -> io::Result<PathBuf> { return getcwd().map(|cwd| cwd.join(path))?.canonicalize(); } // Search PATH to infer current_exe. - if let Some(p) = getenv(OsStr::from_bytes("PATH".as_bytes())) { + if let Some(p) = env::var_os(OsStr::from_bytes("PATH".as_bytes())) { for search_path in split_paths(&p) { let pb = search_path.join(&path); if pb.is_file() diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index d5fbb453c6f..a99c474c763 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2156,6 +2156,7 @@ GetExitCodeProcess GetFileAttributesW GetFileInformationByHandle GetFileInformationByHandleEx +GetFileSizeEx GetFileType GETFINALPATHNAMEBYHANDLE_FLAGS GetFinalPathNameByHandleW diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index eb2914b8644..95bf8040229 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -44,6 +44,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetExitCodeProcess(hprocess : windows_targets::link!("kernel32.dll" "system" fn GetFileAttributesW(lpfilename : PCWSTR) -> u32); windows_targets::link!("kernel32.dll" "system" fn GetFileInformationByHandle(hfile : HANDLE, lpfileinformation : *mut BY_HANDLE_FILE_INFORMATION) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn GetFileInformationByHandleEx(hfile : HANDLE, fileinformationclass : FILE_INFO_BY_HANDLE_CLASS, lpfileinformation : *mut core::ffi::c_void, dwbuffersize : u32) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn GetFileSizeEx(hfile : HANDLE, lpfilesize : *mut i64) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn GetFileType(hfile : HANDLE) -> FILE_TYPE); windows_targets::link!("kernel32.dll" "system" fn GetFinalPathNameByHandleW(hfile : HANDLE, lpszfilepath : PWSTR, cchfilepath : u32, dwflags : GETFINALPATHNAMEBYHANDLE_FLAGS) -> u32); windows_targets::link!("kernel32.dll" "system" fn GetFullPathNameW(lpfilename : PCWSTR, nbufferlength : u32, lpbuffer : PWSTR, lpfilepart : *mut PWSTR) -> u32); diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index 1ebbbec9e91..f331282d2d7 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -202,8 +202,6 @@ fn home_dir_crt() -> Option<PathBuf> { |buf, mut sz| { // GetUserProfileDirectoryW does not quite use the usual protocol for // negotiating the buffer size, so we have to translate. - // FIXME(#141254): We rely on the *undocumented* property that this function will - // always set the size, not just on failure. match c::GetUserProfileDirectoryW( ptr::without_provenance_mut(CURRENT_PROCESS_TOKEN), buf, diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index b012e47f9aa..75e793f18b8 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -86,7 +86,7 @@ const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 #[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))] const UNWIND_DATA_REG: (i32, i32) = (10, 11); // x10, x11 -#[cfg(target_arch = "loongarch64")] +#[cfg(any(target_arch = "loongarch32", target_arch = "loongarch64"))] const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 // The following code is based on GCC's C and C++ personality routines. For reference, see: diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs index a9c2510e6d4..b6777b76668 100644 --- a/library/std/src/sys/process/unix/common.rs +++ b/library/std/src/sys/process/unix/common.rs @@ -1,8 +1,10 @@ #[cfg(all(test, not(target_os = "emscripten")))] mod tests; -use libc::{EXIT_FAILURE, EXIT_SUCCESS, c_char, c_int, gid_t, pid_t, uid_t}; +use libc::{EXIT_FAILURE, EXIT_SUCCESS, c_int, gid_t, pid_t, uid_t}; +pub use self::cstring_array::CStringArray; +use self::cstring_array::CStringIter; use crate::collections::BTreeMap; use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::os::unix::prelude::*; @@ -14,7 +16,9 @@ use crate::sys::fs::OpenOptions; use crate::sys::pipe::{self, AnonPipe}; use crate::sys::process::env::{CommandEnv, CommandEnvs}; use crate::sys_common::{FromInner, IntoInner}; -use crate::{fmt, io, ptr}; +use crate::{fmt, io}; + +mod cstring_array; cfg_if::cfg_if! { if #[cfg(target_os = "fuchsia")] { @@ -77,17 +81,12 @@ cfg_if::cfg_if! { pub struct Command { program: CString, - args: Vec<CString>, - /// Exactly what will be passed to `execvp`. - /// - /// First element is a pointer to `program`, followed by pointers to - /// `args`, followed by a `null`. Be careful when modifying `program` or - /// `args` to properly update this as well. - argv: Argv, + args: CStringArray, env: CommandEnv, program_kind: ProgramKind, cwd: Option<CString>, + chroot: Option<CString>, uid: Option<uid_t>, gid: Option<gid_t>, saw_nul: bool, @@ -101,14 +100,6 @@ pub struct Command { pgroup: Option<pid_t>, } -// Create a new type for argv, so that we can make it `Send` and `Sync` -struct Argv(Vec<*const c_char>); - -// It is safe to make `Argv` `Send` and `Sync`, because it contains -// pointers to memory owned by `Command.args` -unsafe impl Send for Argv {} -unsafe impl Sync for Argv {} - // passed back to std::process with the pipes connected to the child, if any // were requested pub struct StdioPipes { @@ -170,42 +161,19 @@ impl ProgramKind { } impl Command { - #[cfg(not(target_os = "linux"))] pub fn new(program: &OsStr) -> Command { let mut saw_nul = false; let program_kind = ProgramKind::new(program.as_ref()); let program = os2c(program, &mut saw_nul); + let mut args = CStringArray::with_capacity(1); + args.push(program.clone()); Command { - argv: Argv(vec![program.as_ptr(), ptr::null()]), - args: vec![program.clone()], program, - program_kind, + args, env: Default::default(), - cwd: None, - uid: None, - gid: None, - saw_nul, - closures: Vec::new(), - groups: None, - stdin: None, - stdout: None, - stderr: None, - pgroup: None, - } - } - - #[cfg(target_os = "linux")] - pub fn new(program: &OsStr) -> Command { - let mut saw_nul = false; - let program_kind = ProgramKind::new(program.as_ref()); - let program = os2c(program, &mut saw_nul); - Command { - argv: Argv(vec![program.as_ptr(), ptr::null()]), - args: vec![program.clone()], - program, program_kind, - env: Default::default(), cwd: None, + chroot: None, uid: None, gid: None, saw_nul, @@ -214,6 +182,7 @@ impl Command { stdin: None, stdout: None, stderr: None, + #[cfg(target_os = "linux")] create_pidfd: false, pgroup: None, } @@ -222,20 +191,11 @@ impl Command { pub fn set_arg_0(&mut self, arg: &OsStr) { // Set a new arg0 let arg = os2c(arg, &mut self.saw_nul); - debug_assert!(self.argv.0.len() > 1); - self.argv.0[0] = arg.as_ptr(); - self.args[0] = arg; + self.args.write(0, arg); } pub fn arg(&mut self, arg: &OsStr) { - // Overwrite the trailing null pointer in `argv` and then add a new null - // pointer. let arg = os2c(arg, &mut self.saw_nul); - self.argv.0[self.args.len()] = arg.as_ptr(); - self.argv.0.push(ptr::null()); - - // Also make sure we keep track of the owned value to schedule a - // destructor for this memory. self.args.push(arg); } @@ -254,6 +214,12 @@ impl Command { pub fn pgroup(&mut self, pgroup: pid_t) { self.pgroup = Some(pgroup); } + pub fn chroot(&mut self, dir: &Path) { + self.chroot = Some(os2c(dir.as_os_str(), &mut self.saw_nul)); + if self.cwd.is_none() { + self.cwd(&OsStr::new("/")); + } + } #[cfg(target_os = "linux")] pub fn create_pidfd(&mut self, val: bool) { @@ -286,6 +252,8 @@ impl Command { pub fn get_args(&self) -> CommandArgs<'_> { let mut iter = self.args.iter(); + // argv[0] contains the program name, but we are only interested in the + // arguments so skip it. iter.next(); CommandArgs { iter } } @@ -298,12 +266,12 @@ impl Command { self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes()))) } - pub fn get_argv(&self) -> &Vec<*const c_char> { - &self.argv.0 + pub fn get_argv(&self) -> &CStringArray { + &self.args } pub fn get_program_cstr(&self) -> &CStr { - &*self.program + &self.program } #[allow(dead_code)] @@ -326,6 +294,10 @@ impl Command { pub fn get_pgroup(&self) -> Option<pid_t> { self.pgroup } + #[allow(dead_code)] + pub fn get_chroot(&self) -> Option<&CStr> { + self.chroot.as_deref() + } pub fn get_closures(&mut self) -> &mut Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>> { &mut self.closures @@ -392,32 +364,6 @@ fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { }) } -// Helper type to manage ownership of the strings within a C-style array. -pub struct CStringArray { - items: Vec<CString>, - ptrs: Vec<*const c_char>, -} - -impl CStringArray { - pub fn with_capacity(capacity: usize) -> Self { - let mut result = CStringArray { - items: Vec::with_capacity(capacity), - ptrs: Vec::with_capacity(capacity + 1), - }; - result.ptrs.push(ptr::null()); - result - } - pub fn push(&mut self, item: CString) { - let l = self.ptrs.len(); - self.ptrs[l - 1] = item.as_ptr(); - self.ptrs.push(ptr::null()); - self.items.push(item); - } - pub fn as_ptr(&self) -> *const *const c_char { - self.ptrs.as_ptr() - } -} - fn construct_envp(env: BTreeMap<OsString, OsString>, saw_nul: &mut bool) -> CStringArray { let mut result = CStringArray::with_capacity(env.len()); for (mut k, v) in env { @@ -606,14 +552,16 @@ impl fmt::Debug for Command { write!(f, "{}={value:?} ", key.to_string_lossy())?; } } - if self.program != self.args[0] { + + if *self.program != self.args[0] { write!(f, "[{:?}] ", self.program)?; } - write!(f, "{:?}", self.args[0])?; + write!(f, "{:?}", &self.args[0])?; - for arg in &self.args[1..] { + for arg in self.get_args() { write!(f, " {:?}", arg)?; } + Ok(()) } } @@ -645,14 +593,16 @@ impl From<u8> for ExitCode { } pub struct CommandArgs<'a> { - iter: crate::slice::Iter<'a, CString>, + iter: CStringIter<'a>, } impl<'a> Iterator for CommandArgs<'a> { type Item = &'a OsStr; + fn next(&mut self) -> Option<&'a OsStr> { - self.iter.next().map(|cs| OsStr::from_bytes(cs.as_bytes())) + self.iter.next().map(|cs| OsStr::from_bytes(cs.to_bytes())) } + fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } @@ -662,6 +612,7 @@ impl<'a> ExactSizeIterator for CommandArgs<'a> { fn len(&self) -> usize { self.iter.len() } + fn is_empty(&self) -> bool { self.iter.is_empty() } diff --git a/library/std/src/sys/process/unix/common/cstring_array.rs b/library/std/src/sys/process/unix/common/cstring_array.rs new file mode 100644 index 00000000000..1c840a85df9 --- /dev/null +++ b/library/std/src/sys/process/unix/common/cstring_array.rs @@ -0,0 +1,115 @@ +use crate::ffi::{CStr, CString, c_char}; +use crate::ops::Index; +use crate::{fmt, mem, ptr}; + +/// Helper type to manage ownership of the strings within a C-style array. +/// +/// This type manages an array of C-string pointers terminated by a null +/// pointer. The pointer to the array (as returned by `as_ptr`) can be used as +/// a value of `argv` or `environ`. +pub struct CStringArray { + ptrs: Vec<*const c_char>, +} + +impl CStringArray { + /// Creates a new `CStringArray` with enough capacity to hold `capacity` + /// strings. + pub fn with_capacity(capacity: usize) -> Self { + let mut result = CStringArray { ptrs: Vec::with_capacity(capacity + 1) }; + result.ptrs.push(ptr::null()); + result + } + + /// Replace the string at position `index`. + pub fn write(&mut self, index: usize, item: CString) { + let argc = self.ptrs.len() - 1; + let ptr = &mut self.ptrs[..argc][index]; + let old = mem::replace(ptr, item.into_raw()); + // SAFETY: + // `CStringArray` owns all of its strings, and they were all transformed + // into pointers using `CString::into_raw`. Also, this is not the null + // pointer since the indexing above would have failed. + drop(unsafe { CString::from_raw(old.cast_mut()) }); + } + + /// Push an additional string to the array. + pub fn push(&mut self, item: CString) { + let argc = self.ptrs.len() - 1; + // Replace the null pointer at the end of the array... + self.ptrs[argc] = item.into_raw(); + // ... and recreate it to restore the data structure invariant. + self.ptrs.push(ptr::null()); + } + + /// Returns a pointer to the C-string array managed by this type. + pub fn as_ptr(&self) -> *const *const c_char { + self.ptrs.as_ptr() + } + + /// Returns an iterator over all `CStr`s contained in this array. + pub fn iter(&self) -> CStringIter<'_> { + CStringIter { iter: self.ptrs[..self.ptrs.len() - 1].iter() } + } +} + +impl Index<usize> for CStringArray { + type Output = CStr; + fn index(&self, index: usize) -> &CStr { + let ptr = self.ptrs[..self.ptrs.len() - 1][index]; + // SAFETY: + // `CStringArray` owns all of its strings. Also, this is not the null + // pointer since the indexing above would have failed. + unsafe { CStr::from_ptr(ptr) } + } +} + +impl fmt::Debug for CStringArray { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + +// SAFETY: `CStringArray` is basically just a `Vec<CString>` +unsafe impl Send for CStringArray {} +// SAFETY: `CStringArray` is basically just a `Vec<CString>` +unsafe impl Sync for CStringArray {} + +impl Drop for CStringArray { + fn drop(&mut self) { + // SAFETY: + // `CStringArray` owns all of its strings, and they were all transformed + // into pointers using `CString::into_raw`. + self.ptrs[..self.ptrs.len() - 1] + .iter() + .for_each(|&p| drop(unsafe { CString::from_raw(p.cast_mut()) })) + } +} + +/// An iterator over all `CStr`s contained in a `CStringArray`. +#[derive(Clone)] +pub struct CStringIter<'a> { + iter: crate::slice::Iter<'a, *const c_char>, +} + +impl<'a> Iterator for CStringIter<'a> { + type Item = &'a CStr; + fn next(&mut self) -> Option<&'a CStr> { + // SAFETY: + // `CStringArray` owns all of its strings. Also, this is not the null + // pointer since the last element is excluded when creating `iter`. + self.iter.next().map(|&p| unsafe { CStr::from_ptr(p) }) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } +} + +impl<'a> ExactSizeIterator for CStringIter<'a> { + fn len(&self) -> usize { + self.iter.len() + } + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 1b3bd2de265..4f595ac9a1c 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -323,6 +323,15 @@ impl Command { cvt(libc::setuid(u as uid_t))?; } } + if let Some(chroot) = self.get_chroot() { + #[cfg(not(target_os = "fuchsia"))] + cvt(libc::chroot(chroot.as_ptr()))?; + #[cfg(target_os = "fuchsia")] + return Err(io::const_error!( + io::ErrorKind::Unsupported, + "chroot not supported by fuchsia" + )); + } if let Some(cwd) = self.get_cwd() { cvt(libc::chdir(cwd.as_ptr()))?; } @@ -447,6 +456,7 @@ impl Command { || (self.env_saw_path() && !self.program_is_path()) || !self.get_closures().is_empty() || self.get_groups().is_some() + || self.get_chroot().is_some() { return Ok(None); } diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs index fab3b36ebf3..f33b4a375da 100644 --- a/library/std/src/sys/process/unix/vxworks.rs +++ b/library/std/src/sys/process/unix/vxworks.rs @@ -27,6 +27,12 @@ impl Command { "nul byte found in provided data", )); } + if self.get_chroot().is_some() { + return Err(io::const_error!( + ErrorKind::Unsupported, + "chroot not supported by vxworks", + )); + } let (ours, theirs) = self.setup_io(default, needs_stdin)?; let mut p = Process { pid: 0, status: None }; diff --git a/library/std/src/sys/sync/mutex/futex.rs b/library/std/src/sys/sync/mutex/futex.rs index ce9b2daa5f8..70e2ea9f605 100644 --- a/library/std/src/sys/sync/mutex/futex.rs +++ b/library/std/src/sys/sync/mutex/futex.rs @@ -19,11 +19,15 @@ impl Mutex { } #[inline] + // Make this a diagnostic item for Miri's concurrency model checker. + #[cfg_attr(not(test), rustc_diagnostic_item = "sys_mutex_try_lock")] pub fn try_lock(&self) -> bool { self.futex.compare_exchange(UNLOCKED, LOCKED, Acquire, Relaxed).is_ok() } #[inline] + // Make this a diagnostic item for Miri's concurrency model checker. + #[cfg_attr(not(test), rustc_diagnostic_item = "sys_mutex_lock")] pub fn lock(&self) { if self.futex.compare_exchange(UNLOCKED, LOCKED, Acquire, Relaxed).is_err() { self.lock_contended(); @@ -80,6 +84,8 @@ impl Mutex { } #[inline] + // Make this a diagnostic item for Miri's concurrency model checker. + #[cfg_attr(not(test), rustc_diagnostic_item = "sys_mutex_unlock")] pub unsafe fn unlock(&self) { if self.futex.swap(UNLOCKED, Release) == CONTENDED { // We only wake up one thread. When that thread locks the mutex, it diff --git a/library/std/src/sys/sync/mutex/pthread.rs b/library/std/src/sys/sync/mutex/pthread.rs index 75b4b9c6dad..a7a3b47d0ec 100644 --- a/library/std/src/sys/sync/mutex/pthread.rs +++ b/library/std/src/sys/sync/mutex/pthread.rs @@ -6,7 +6,7 @@ use crate::sys::pal::sync as pal; use crate::sys::sync::OnceBox; pub struct Mutex { - pub pal: OnceBox<pal::Mutex>, + pub(in crate::sys::sync) pal: OnceBox<pal::Mutex>, } impl Mutex { @@ -28,6 +28,8 @@ impl Mutex { } #[inline] + // Make this a diagnostic item for Miri's concurrency model checker. + #[cfg_attr(not(test), rustc_diagnostic_item = "sys_mutex_lock")] pub fn lock(&self) { // SAFETY: we call `init` above, therefore reentrant locking is safe. // In `drop` we ensure that the mutex is not destroyed while locked. @@ -35,6 +37,8 @@ impl Mutex { } #[inline] + // Make this a diagnostic item for Miri's concurrency model checker. + #[cfg_attr(not(test), rustc_diagnostic_item = "sys_mutex_unlock")] pub unsafe fn unlock(&self) { // SAFETY: the mutex can only be locked if it is already initialized // and we observed this initialization since we observed the locking. @@ -42,6 +46,8 @@ impl Mutex { } #[inline] + // Make this a diagnostic item for Miri's concurrency model checker. + #[cfg_attr(not(test), rustc_diagnostic_item = "sys_mutex_try_lock")] pub fn try_lock(&self) -> bool { // SAFETY: we call `init` above, therefore reentrant locking is safe. // In `drop` we ensure that the mutex is not destroyed while locked. diff --git a/library/std/src/sys/thread_local/guard/key.rs b/library/std/src/sys/thread_local/guard/key.rs index 59581e6f281..f91471419c1 100644 --- a/library/std/src/sys/thread_local/guard/key.rs +++ b/library/std/src/sys/thread_local/guard/key.rs @@ -32,7 +32,7 @@ pub fn enable() { /// On platforms with key-based TLS, the system runs the destructors for us. /// We still have to make sure that [`crate::rt::thread_cleanup`] is called, -/// however. This is done by defering the execution of a TLS destructor to +/// however. This is done by deferring the execution of a TLS destructor to /// the next round of destruction inside the TLS destructors. #[cfg(not(target_thread_local))] pub fn enable() { @@ -46,7 +46,7 @@ pub fn enable() { unsafe extern "C" fn run(state: *mut u8) { if state == DEFER { // Make sure that this function is run again in the next round of - // TLS destruction. If there is no futher round, there will be leaks, + // TLS destruction. If there is no further round, there will be leaks, // but that's okay, `thread_cleanup` is not guaranteed to be called. unsafe { set(CLEANUP.force(), RUN) } } else { diff --git a/library/std/src/sys/thread_local/key/unix.rs b/library/std/src/sys/thread_local/key/unix.rs index 93bd0d1f668..8fa24265e43 100644 --- a/library/std/src/sys/thread_local/key/unix.rs +++ b/library/std/src/sys/thread_local/key/unix.rs @@ -25,7 +25,9 @@ pub type Key = libc::pthread_key_t; #[inline] pub fn create(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key { let mut key = 0; - assert_eq!(unsafe { libc::pthread_key_create(&mut key, mem::transmute(dtor)) }, 0); + if unsafe { libc::pthread_key_create(&mut key, mem::transmute(dtor)) } != 0 { + rtabort!("out of TLS keys"); + } key } diff --git a/library/std/src/sys/thread_local/key/windows.rs b/library/std/src/sys/thread_local/key/windows.rs index c34c7bc204f..2ff0fd1196e 100644 --- a/library/std/src/sys/thread_local/key/windows.rs +++ b/library/std/src/sys/thread_local/key/windows.rs @@ -81,15 +81,10 @@ impl LazyKey { } else { let key = unsafe { c::TlsAlloc() }; if key == c::TLS_OUT_OF_INDEXES { - // Wakeup the waiting threads before panicking to avoid deadlock. - unsafe { - c::InitOnceComplete( - self.once.get(), - c::INIT_ONCE_INIT_FAILED, - ptr::null_mut(), - ); - } - panic!("out of TLS indexes"); + // Since we abort the process, there is no need to wake up + // the waiting threads. If this were a panic, the wakeup + // would need to occur first in order to avoid deadlock. + rtabort!("out of TLS indexes"); } unsafe { @@ -112,7 +107,9 @@ impl LazyKey { // If there is no destructor to clean up, we can use racy initialization. let key = unsafe { c::TlsAlloc() }; - assert_ne!(key, c::TLS_OUT_OF_INDEXES, "out of TLS indexes"); + if key == c::TLS_OUT_OF_INDEXES { + rtabort!("out of TLS indexes"); + } match self.key.compare_exchange(0, key + 1, AcqRel, Acquire) { Ok(_) => key, diff --git a/library/std/src/sys/thread_local/native/lazy.rs b/library/std/src/sys/thread_local/native/lazy.rs index 51294285ba0..b556dd9aa25 100644 --- a/library/std/src/sys/thread_local/native/lazy.rs +++ b/library/std/src/sys/thread_local/native/lazy.rs @@ -1,9 +1,9 @@ -use crate::cell::UnsafeCell; -use crate::hint::unreachable_unchecked; +use crate::cell::{Cell, UnsafeCell}; +use crate::mem::MaybeUninit; use crate::ptr; use crate::sys::thread_local::{abort_on_dtor_unwind, destructors}; -pub unsafe trait DestroyedState: Sized { +pub unsafe trait DestroyedState: Sized + Copy { fn register_dtor<T>(s: &Storage<T, Self>); } @@ -19,15 +19,17 @@ unsafe impl DestroyedState for () { } } -enum State<T, D> { - Initial, - Alive(T), +#[derive(Copy, Clone)] +enum State<D> { + Uninitialized, + Alive, Destroyed(D), } #[allow(missing_debug_implementations)] pub struct Storage<T, D> { - state: UnsafeCell<State<T, D>>, + state: Cell<State<D>>, + value: UnsafeCell<MaybeUninit<T>>, } impl<T, D> Storage<T, D> @@ -35,7 +37,10 @@ where D: DestroyedState, { pub const fn new() -> Storage<T, D> { - Storage { state: UnsafeCell::new(State::Initial) } + Storage { + state: Cell::new(State::Uninitialized), + value: UnsafeCell::new(MaybeUninit::uninit()), + } } /// Gets a pointer to the TLS value, potentially initializing it with the @@ -49,35 +54,49 @@ where /// The `self` reference must remain valid until the TLS destructor is run. #[inline] pub unsafe fn get_or_init(&self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T { - let state = unsafe { &*self.state.get() }; - match state { - State::Alive(v) => v, - State::Destroyed(_) => ptr::null(), - State::Initial => unsafe { self.initialize(i, f) }, + if let State::Alive = self.state.get() { + self.value.get().cast() + } else { + unsafe { self.get_or_init_slow(i, f) } } } + /// # Safety + /// The `self` reference must remain valid until the TLS destructor is run. #[cold] - unsafe fn initialize(&self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T { - // Perform initialization + unsafe fn get_or_init_slow( + &self, + i: Option<&mut Option<T>>, + f: impl FnOnce() -> T, + ) -> *const T { + match self.state.get() { + State::Uninitialized => {} + State::Alive => return self.value.get().cast(), + State::Destroyed(_) => return ptr::null(), + } let v = i.and_then(Option::take).unwrap_or_else(f); - let old = unsafe { self.state.get().replace(State::Alive(v)) }; - match old { + // SAFETY: we cannot be inside a `LocalKey::with` scope, as the initializer + // has already returned and the next scope only starts after we return + // the pointer. Therefore, there can be no references to the old value, + // even if it was initialized. Thus because we are !Sync we have exclusive + // access to self.value and may replace it. + let mut old_value = unsafe { self.value.get().replace(MaybeUninit::new(v)) }; + match self.state.replace(State::Alive) { // If the variable is not being recursively initialized, register // the destructor. This might be a noop if the value does not need // destruction. - State::Initial => D::register_dtor(self), - // Else, drop the old value. This might be changed to a panic. - val => drop(val), - } + State::Uninitialized => D::register_dtor(self), - // SAFETY: the state was just set to `Alive` - unsafe { - let State::Alive(v) = &*self.state.get() else { unreachable_unchecked() }; - v + // Recursive initialization, we only need to drop the old value + // as we've already registered the destructor. + State::Alive => unsafe { old_value.assume_init_drop() }, + + State::Destroyed(_) => unreachable!(), } + + self.value.get().cast() } } @@ -92,9 +111,12 @@ unsafe extern "C" fn destroy<T>(ptr: *mut u8) { // Print a nice abort message if a panic occurs. abort_on_dtor_unwind(|| { let storage = unsafe { &*(ptr as *const Storage<T, ()>) }; - // Update the state before running the destructor as it may attempt to - // access the variable. - let val = unsafe { storage.state.get().replace(State::Destroyed(())) }; - drop(val); + if let State::Alive = storage.state.replace(State::Destroyed(())) { + // SAFETY: we ensured the state was Alive so the value was initialized. + // We also updated the state to Destroyed to prevent the destructor + // from accessing the thread-local variable, as this would violate + // the exclusive access provided by &mut T in Drop::drop. + unsafe { (*storage.value.get()).assume_init_drop() } + } }) } |
