From c3b602af8ce5800971c323f5eead66504c0ecce7 Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Sat, 6 Jul 2024 09:09:10 +0200 Subject: [PATCH 01/36] Add missing try_new_uninit_slice_in and try_new_zeroed_slice_in The methods for fallible slice allocation in a given allocator were missing, which was an oversight according to https://github.com/rust-lang/wg-allocators/issues/130 This PR adds them as `try_new_uninit_slice_in` and `try_new_zeroed_slice_in`. Also adds missing punctuation to the doc comments of ` try_new_uninit_slice` and `try_new_zeroed_slice` --- alloc/src/boxed.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/alloc/src/boxed.rs b/alloc/src/boxed.rs index 1ec095a46f704..d67b0ac37eb1f 100644 --- a/alloc/src/boxed.rs +++ b/alloc/src/boxed.rs @@ -704,7 +704,7 @@ impl Box<[T]> { } /// Constructs a new boxed slice with uninitialized contents. Returns an error if - /// the allocation fails + /// the allocation fails. /// /// # Examples /// @@ -739,7 +739,7 @@ impl Box<[T]> { } /// Constructs a new boxed slice with uninitialized contents, with the memory - /// being filled with `0` bytes. Returns an error if the allocation fails + /// being filled with `0` bytes. Returns an error if the allocation fails. /// /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage /// of this method. @@ -831,6 +831,79 @@ impl Box<[T], A> { pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit], A> { unsafe { RawVec::with_capacity_zeroed_in(len, alloc).into_box(len) } } + + /// Constructs a new boxed slice with uninitialized contents in the provided allocator. Returns an error if + /// the allocation fails. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// use std::alloc::System; + /// + /// let mut values = Box::<[u32]>::try_new_uninit_slice(3, System)?; + /// let values = unsafe { + /// // Deferred initialization: + /// values[0].as_mut_ptr().write(1); + /// values[1].as_mut_ptr().write(2); + /// values[2].as_mut_ptr().write(3); + /// values.assume_init() + /// }; + /// + /// assert_eq!(*values, [1, 2, 3]); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn try_new_uninit_slice_in(len: usize, alloc: A) -> Result], A>, AllocError> { + let ptr = if T::IS_ZST || len == 0 { + NonNull::dangling() + } else { + let layout = match Layout::array::>(len) { + Ok(l) => l, + Err(_) => return Err(AllocError), + }; + Global.allocate(layout)?.cast() + }; + unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, alloc).into_box(len)) } + } + + /// Constructs a new boxed slice with uninitialized contents in the provided allocator, with the memory + /// being filled with `0` bytes. Returns an error if the allocation fails. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// use std::alloc::System; + /// + /// let values = Box::<[u32]>::try_new_zeroed_slice(3, System)?; + /// let values = unsafe { values.assume_init() }; + /// + /// assert_eq!(*values, [0, 0, 0]); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn try_new_zeroed_slice_in(len: usize, alloc: A) -> Result], A>, AllocError> { + let ptr = if T::IS_ZST || len == 0 { + NonNull::dangling() + } else { + let layout = match Layout::array::>(len) { + Ok(l) => l, + Err(_) => return Err(AllocError), + }; + Global.allocate_zeroed(layout)?.cast() + }; + unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, alloc).into_box(len)) } + } } impl Box, A> { From 8d0199656c4137b9e3d6c34f8996ab1c4f4bda47 Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Sat, 6 Jul 2024 14:38:00 +0200 Subject: [PATCH 02/36] Run formatter on alloc/src/boxed.rs --- alloc/src/boxed.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/alloc/src/boxed.rs b/alloc/src/boxed.rs index d67b0ac37eb1f..b817cf855a5e7 100644 --- a/alloc/src/boxed.rs +++ b/alloc/src/boxed.rs @@ -839,7 +839,7 @@ impl Box<[T], A> { /// /// ``` /// #![feature(allocator_api, new_uninit)] - /// + /// /// use std::alloc::System; /// /// let mut values = Box::<[u32]>::try_new_uninit_slice(3, System)?; @@ -856,7 +856,10 @@ impl Box<[T], A> { /// ``` #[unstable(feature = "allocator_api", issue = "32838")] #[inline] - pub fn try_new_uninit_slice_in(len: usize, alloc: A) -> Result], A>, AllocError> { + pub fn try_new_uninit_slice_in( + len: usize, + alloc: A, + ) -> Result], A>, AllocError> { let ptr = if T::IS_ZST || len == 0 { NonNull::dangling() } else { @@ -879,7 +882,7 @@ impl Box<[T], A> { /// /// ``` /// #![feature(allocator_api, new_uninit)] - /// + /// /// use std::alloc::System; /// /// let values = Box::<[u32]>::try_new_zeroed_slice(3, System)?; @@ -892,7 +895,10 @@ impl Box<[T], A> { /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] #[inline] - pub fn try_new_zeroed_slice_in(len: usize, alloc: A) -> Result], A>, AllocError> { + pub fn try_new_zeroed_slice_in( + len: usize, + alloc: A, + ) -> Result], A>, AllocError> { let ptr = if T::IS_ZST || len == 0 { NonNull::dangling() } else { From be23cef3a427257417387da460fdd7100f11c0c2 Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Sat, 6 Jul 2024 18:50:03 +0200 Subject: [PATCH 03/36] Fix doc examples --- alloc/src/boxed.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alloc/src/boxed.rs b/alloc/src/boxed.rs index b817cf855a5e7..847594103d50d 100644 --- a/alloc/src/boxed.rs +++ b/alloc/src/boxed.rs @@ -842,7 +842,7 @@ impl Box<[T], A> { /// /// use std::alloc::System; /// - /// let mut values = Box::<[u32]>::try_new_uninit_slice(3, System)?; + /// let mut values = Box::<[u32]>::try_new_uninit_slice_in(3, System)?; /// let values = unsafe { /// // Deferred initialization: /// values[0].as_mut_ptr().write(1); @@ -885,7 +885,7 @@ impl Box<[T], A> { /// /// use std::alloc::System; /// - /// let values = Box::<[u32]>::try_new_zeroed_slice(3, System)?; + /// let values = Box::<[u32]>::try_new_zeroed_slice_in(3, System)?; /// let values = unsafe { values.assume_init() }; /// /// assert_eq!(*values, [0, 0, 0]); From ca537d2cb337ab77c3f21d2802f956e363adfebf Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Sat, 6 Jul 2024 19:13:53 +0200 Subject: [PATCH 04/36] Fix them doc examples some more Apologies for the many attempts, my dev loop for this consists of editing on github, committing, and then waiting for the CI failure log to yell at me. --- alloc/src/boxed.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alloc/src/boxed.rs b/alloc/src/boxed.rs index 847594103d50d..322c0756abdb3 100644 --- a/alloc/src/boxed.rs +++ b/alloc/src/boxed.rs @@ -842,7 +842,7 @@ impl Box<[T], A> { /// /// use std::alloc::System; /// - /// let mut values = Box::<[u32]>::try_new_uninit_slice_in(3, System)?; + /// let mut values = Box::<[u32], _>::try_new_uninit_slice_in(3, System)?; /// let values = unsafe { /// // Deferred initialization: /// values[0].as_mut_ptr().write(1); @@ -885,7 +885,7 @@ impl Box<[T], A> { /// /// use std::alloc::System; /// - /// let values = Box::<[u32]>::try_new_zeroed_slice_in(3, System)?; + /// let values = Box::<[u32], _>::try_new_zeroed_slice_in(3, System)?; /// let values = unsafe { values.assume_init() }; /// /// assert_eq!(*values, [0, 0, 0]); From 9bbf09d3d07933db77ec134971739b867409b23d Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sun, 14 Jul 2024 20:21:01 +0000 Subject: [PATCH 05/36] Windows: move BSD socket shims to netc --- std/src/sys/pal/windows/c.rs | 97 +------------------- std/src/sys/pal/windows/c/bindings.txt | 1 + std/src/sys/pal/windows/c/windows_sys.rs | 8 ++ std/src/sys/pal/windows/net.rs | 112 ++++++++++++++++++++--- 4 files changed, 109 insertions(+), 109 deletions(-) diff --git a/std/src/sys/pal/windows/c.rs b/std/src/sys/pal/windows/c.rs index 296d19a926d96..84f3d6a5399bc 100644 --- a/std/src/sys/pal/windows/c.rs +++ b/std/src/sys/pal/windows/c.rs @@ -8,7 +8,7 @@ use crate::ffi::CStr; use crate::mem; -use crate::os::raw::{c_char, c_int, c_uint, c_ulong, c_ushort, c_void}; +use crate::os::raw::{c_uint, c_ulong, c_ushort, c_void}; use crate::os::windows::io::{AsRawHandle, BorrowedHandle}; use crate::ptr; @@ -19,12 +19,6 @@ pub use windows_sys::*; pub type WCHAR = u16; -pub type socklen_t = c_int; -pub type ADDRESS_FAMILY = c_ushort; -pub use FD_SET as fd_set; -pub use LINGER as linger; -pub use TIMEVAL as timeval; - pub const INVALID_HANDLE_VALUE: HANDLE = ::core::ptr::without_provenance_mut(-1i32 as _); // https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure?view=msvc-170 @@ -42,20 +36,6 @@ pub const INIT_ONCE_STATIC_INIT: INIT_ONCE = INIT_ONCE { Ptr: ptr::null_mut() }; pub const OBJ_DONT_REPARSE: u32 = windows_sys::OBJ_DONT_REPARSE as u32; pub const FRS_ERR_SYSVOL_POPULATE_TIMEOUT: u32 = windows_sys::FRS_ERR_SYSVOL_POPULATE_TIMEOUT as u32; -pub const AF_INET: c_int = windows_sys::AF_INET as c_int; -pub const AF_INET6: c_int = windows_sys::AF_INET6 as c_int; - -#[repr(C)] -pub struct ip_mreq { - pub imr_multiaddr: in_addr, - pub imr_interface: in_addr, -} - -#[repr(C)] -pub struct ipv6_mreq { - pub ipv6mr_multiaddr: in6_addr, - pub ipv6mr_interface: c_uint, -} // Equivalent to the `NT_SUCCESS` C preprocessor macro. // See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values @@ -127,45 +107,6 @@ pub struct MOUNT_POINT_REPARSE_BUFFER { pub PathBuffer: WCHAR, } -#[repr(C)] -pub struct SOCKADDR_STORAGE_LH { - pub ss_family: ADDRESS_FAMILY, - pub __ss_pad1: [c_char; 6], - pub __ss_align: i64, - pub __ss_pad2: [c_char; 112], -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct sockaddr_in { - pub sin_family: ADDRESS_FAMILY, - pub sin_port: c_ushort, - pub sin_addr: in_addr, - pub sin_zero: [c_char; 8], -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct sockaddr_in6 { - pub sin6_family: ADDRESS_FAMILY, - pub sin6_port: c_ushort, - pub sin6_flowinfo: c_ulong, - pub sin6_addr: in6_addr, - pub sin6_scope_id: c_ulong, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct in_addr { - pub s_addr: u32, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct in6_addr { - pub s6_addr: [u8; 16], -} - // Desktop specific functions & types cfg_if::cfg_if! { if #[cfg(not(target_vendor = "uwp"))] { @@ -205,42 +146,6 @@ pub unsafe extern "system" fn ReadFileEx( ) } -// POSIX compatibility shims. -pub unsafe fn recv(socket: SOCKET, buf: *mut c_void, len: c_int, flags: c_int) -> c_int { - windows_sys::recv(socket, buf.cast::(), len, flags) -} -pub unsafe fn send(socket: SOCKET, buf: *const c_void, len: c_int, flags: c_int) -> c_int { - windows_sys::send(socket, buf.cast::(), len, flags) -} -pub unsafe fn recvfrom( - socket: SOCKET, - buf: *mut c_void, - len: c_int, - flags: c_int, - addr: *mut SOCKADDR, - addrlen: *mut c_int, -) -> c_int { - windows_sys::recvfrom(socket, buf.cast::(), len, flags, addr, addrlen) -} -pub unsafe fn sendto( - socket: SOCKET, - buf: *const c_void, - len: c_int, - flags: c_int, - addr: *const SOCKADDR, - addrlen: c_int, -) -> c_int { - windows_sys::sendto(socket, buf.cast::(), len, flags, addr, addrlen) -} -pub unsafe fn getaddrinfo( - node: *const c_char, - service: *const c_char, - hints: *const ADDRINFOA, - res: *mut *mut ADDRINFOA, -) -> c_int { - windows_sys::getaddrinfo(node.cast::(), service.cast::(), hints, res) -} - cfg_if::cfg_if! { if #[cfg(not(target_vendor = "uwp"))] { pub unsafe fn NtReadFile( diff --git a/std/src/sys/pal/windows/c/bindings.txt b/std/src/sys/pal/windows/c/bindings.txt index 5ad4a3731d822..794e2c90c5242 100644 --- a/std/src/sys/pal/windows/c/bindings.txt +++ b/std/src/sys/pal/windows/c/bindings.txt @@ -2059,6 +2059,7 @@ Windows.Win32.Networking.WinSock.SOCK_RDM Windows.Win32.Networking.WinSock.SOCK_SEQPACKET Windows.Win32.Networking.WinSock.SOCK_STREAM Windows.Win32.Networking.WinSock.SOCKADDR +Windows.Win32.Networking.WinSock.SOCKADDR_STORAGE Windows.Win32.Networking.WinSock.SOCKADDR_UN Windows.Win32.Networking.WinSock.SOCKET Windows.Win32.Networking.WinSock.SOCKET_ERROR diff --git a/std/src/sys/pal/windows/c/windows_sys.rs b/std/src/sys/pal/windows/c/windows_sys.rs index fea00fec9ae59..eae0f77586066 100644 --- a/std/src/sys/pal/windows/c/windows_sys.rs +++ b/std/src/sys/pal/windows/c/windows_sys.rs @@ -2890,6 +2890,14 @@ pub struct SOCKADDR { } #[repr(C)] #[derive(Clone, Copy)] +pub struct SOCKADDR_STORAGE { + pub ss_family: ADDRESS_FAMILY, + pub __ss_pad1: [i8; 6], + pub __ss_align: i64, + pub __ss_pad2: [i8; 112], +} +#[repr(C)] +#[derive(Clone, Copy)] pub struct SOCKADDR_UN { pub sun_family: ADDRESS_FAMILY, pub sun_path: [i8; 108], diff --git a/std/src/sys/pal/windows/net.rs b/std/src/sys/pal/windows/net.rs index d51fb56238f2c..b7ecff032e4af 100644 --- a/std/src/sys/pal/windows/net.rs +++ b/std/src/sys/pal/windows/net.rs @@ -17,14 +17,100 @@ use crate::time::Duration; use core::ffi::{c_int, c_long, c_ulong, c_ushort}; +#[allow(non_camel_case_types)] pub type wrlen_t = i32; pub mod netc { - pub use crate::sys::c::ADDRESS_FAMILY as sa_family_t; - pub use crate::sys::c::ADDRINFOA as addrinfo; - pub use crate::sys::c::SOCKADDR as sockaddr; - pub use crate::sys::c::SOCKADDR_STORAGE_LH as sockaddr_storage; - pub use crate::sys::c::*; + //! BSD socket compatibility shim + //! + //! Some Windows API types are not quite what's expected by our cross-platform + //! net code. E.g. naming differences or different pointer types. + use crate::sys::c::{self, ADDRESS_FAMILY, ADDRINFOA, SOCKADDR, SOCKET}; + use core::ffi::{c_char, c_int, c_uint, c_ulong, c_ushort, c_void}; + + // re-exports from Windows API bindings. + pub use crate::sys::c::{ + bind, connect, freeaddrinfo, getpeername, getsockname, getsockopt, listen, setsockopt, + ADDRESS_FAMILY as sa_family_t, ADDRINFOA as addrinfo, IPPROTO_IP, IPPROTO_IPV6, + IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, + IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, + SOCKADDR as sockaddr, SOCKADDR_STORAGE as sockaddr_storage, SOCK_DGRAM, SOCK_STREAM, + SOL_SOCKET, SO_BROADCAST, SO_RCVTIMEO, SO_SNDTIMEO, + }; + + #[allow(non_camel_case_types)] + pub type socklen_t = c_int; + + pub const AF_INET: i32 = c::AF_INET as i32; + pub const AF_INET6: i32 = c::AF_INET6 as i32; + + // The following two structs use a union in the generated bindings but + // our cross-platform code expects a normal field so it's redefined here. + // As a consequence, we also need to redefine other structs that use this struct. + #[repr(C)] + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[repr(C)] + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[repr(C)] + pub struct ip_mreq { + pub imr_multiaddr: in_addr, + pub imr_interface: in_addr, + } + + #[repr(C)] + pub struct ipv6_mreq { + pub ipv6mr_multiaddr: in6_addr, + pub ipv6mr_interface: c_uint, + } + + #[repr(C)] + #[derive(Copy, Clone)] + pub struct sockaddr_in { + pub sin_family: ADDRESS_FAMILY, + pub sin_port: c_ushort, + pub sin_addr: in_addr, + pub sin_zero: [c_char; 8], + } + + #[repr(C)] + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + pub sin6_family: ADDRESS_FAMILY, + pub sin6_port: c_ushort, + pub sin6_flowinfo: c_ulong, + pub sin6_addr: in6_addr, + pub sin6_scope_id: c_ulong, + } + + pub unsafe fn send(socket: SOCKET, buf: *const c_void, len: c_int, flags: c_int) -> c_int { + unsafe { c::send(socket, buf.cast::(), len, flags) } + } + pub unsafe fn sendto( + socket: SOCKET, + buf: *const c_void, + len: c_int, + flags: c_int, + addr: *const SOCKADDR, + addrlen: c_int, + ) -> c_int { + unsafe { c::sendto(socket, buf.cast::(), len, flags, addr, addrlen) } + } + pub unsafe fn getaddrinfo( + node: *const c_char, + service: *const c_char, + hints: *const ADDRINFOA, + res: *mut *mut ADDRINFOA, + ) -> c_int { + unsafe { c::getaddrinfo(node.cast::(), service.cast::(), hints, res) } + } } pub struct Socket(OwnedSocket); @@ -102,8 +188,8 @@ where impl Socket { pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { let family = match *addr { - SocketAddr::V4(..) => c::AF_INET, - SocketAddr::V6(..) => c::AF_INET6, + SocketAddr::V4(..) => netc::AF_INET, + SocketAddr::V6(..) => netc::AF_INET6, }; let socket = unsafe { c::WSASocketW( @@ -157,7 +243,7 @@ impl Socket { return Err(io::Error::ZERO_TIMEOUT); } - let mut timeout = c::timeval { + let mut timeout = c::TIMEVAL { tv_sec: cmp::min(timeout.as_secs(), c_long::MAX as u64) as c_long, tv_usec: timeout.subsec_micros() as c_long, }; @@ -167,7 +253,7 @@ impl Socket { } let fds = { - let mut fds = unsafe { mem::zeroed::() }; + let mut fds = unsafe { mem::zeroed::() }; fds.fd_count = 1; fds.fd_array[0] = self.as_raw(); fds @@ -295,8 +381,8 @@ impl Socket { buf: &mut [u8], flags: c_int, ) -> io::Result<(usize, SocketAddr)> { - let mut storage = unsafe { mem::zeroed::() }; - let mut addrlen = mem::size_of_val(&storage) as c::socklen_t; + let mut storage = unsafe { mem::zeroed::() }; + let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; let length = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; // On unix when a socket is shut down all further reads return 0, so we @@ -399,7 +485,7 @@ impl Socket { } pub fn set_linger(&self, linger: Option) -> io::Result<()> { - let linger = c::linger { + let linger = c::LINGER { l_onoff: linger.is_some() as c_ushort, l_linger: linger.unwrap_or_default().as_secs() as c_ushort, }; @@ -408,7 +494,7 @@ impl Socket { } pub fn linger(&self) -> io::Result> { - let val: c::linger = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; + let val: c::LINGER = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } From 857ed93c04e99410ae1d9d3e6ab620cc5aeb8e59 Mon Sep 17 00:00:00 2001 From: Boxy Date: Sun, 14 Jul 2024 12:50:41 +0100 Subject: [PATCH 06/36] Forbid `!Sized` types and references --- core/src/marker.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/core/src/marker.rs b/core/src/marker.rs index 21abd7c036ba7..70bd4f67ebfde 100644 --- a/core/src/marker.rs +++ b/core/src/marker.rs @@ -995,13 +995,22 @@ marker_impls! { isize, i8, i16, i32, i64, i128, bool, char, - str /* Technically requires `[u8]: ConstParamTy` */, (), {T: ConstParamTy, const N: usize} [T; N], - {T: ConstParamTy} [T], - {T: ?Sized + ConstParamTy} &T, } +#[unstable(feature = "adt_const_params", issue = "95174")] +#[rustc_reservation_impl = "types that are not `Sized` are not supported as the type of a const generic parameter"] +impl ConstParamTy for [T] {} + +#[unstable(feature = "adt_const_params", issue = "95174")] +#[rustc_reservation_impl = "types that are not `Sized` are not supported as the type of a const generic parameter"] +impl ConstParamTy for str {} + +#[unstable(feature = "adt_const_params", issue = "95174")] +#[rustc_reservation_impl = "references are not supported as the type of a const generic parameter"] +impl ConstParamTy for &T {} + /// A common trait implemented by all function pointers. #[unstable( feature = "fn_ptr_trait", From be0c06bc63e42bfed60a45e442039a3924af3d04 Mon Sep 17 00:00:00 2001 From: Boxy Date: Sun, 14 Jul 2024 13:38:51 +0100 Subject: [PATCH 07/36] Split part of `adt_const_params` into `unsized_const_params` --- core/src/lib.rs | 1 + core/src/marker.rs | 61 +++++++++++++++++++++++++-------- core/src/mem/transmutability.rs | 6 ++-- core/src/tuple.rs | 14 ++++++-- 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index 49f89e702558f..d0622182ea969 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -248,6 +248,7 @@ #![feature(transparent_unions)] #![feature(try_blocks)] #![feature(unboxed_closures)] +#![feature(unsized_const_params)] #![feature(unsized_fn_params)] #![feature(with_negative_coherence)] // tidy-alphabetical-end diff --git a/core/src/marker.rs b/core/src/marker.rs index 70bd4f67ebfde..a87528033c03b 100644 --- a/core/src/marker.rs +++ b/core/src/marker.rs @@ -975,41 +975,74 @@ pub trait PointerLike {} /// that all fields are also `ConstParamTy`, which implies that recursively, all fields /// are `StructuralPartialEq`. #[lang = "const_param_ty"] -#[unstable(feature = "adt_const_params", issue = "95174")] +#[unstable(feature = "unsized_const_params", issue = "95174")] #[diagnostic::on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] #[allow(multiple_supertrait_upcastable)] -pub trait ConstParamTy: StructuralPartialEq + Eq {} +// We name this differently than the derive macro so that the `adt_const_params` can +// be used independently of `unsized_const_params` without requiring a full path +// to the derive macro every time it is used. This should be renamed on stabilization. +pub trait ConstParamTy_: UnsizedConstParamTy + StructuralPartialEq + Eq {} /// Derive macro generating an impl of the trait `ConstParamTy`. #[rustc_builtin_macro] +#[allow_internal_unstable(unsized_const_params)] #[unstable(feature = "adt_const_params", issue = "95174")] pub macro ConstParamTy($item:item) { /* compiler built-in */ } +#[cfg_attr(not(bootstrap), lang = "unsized_const_param_ty")] +#[unstable(feature = "unsized_const_params", issue = "95174")] +#[diagnostic::on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] +/// A marker for types which can be used as types of `const` generic parameters. +/// +/// Equivalent to [`ConstParamTy_`] except that this is used by +/// the `unsized_const_params` to allow for fake unstable impls. +pub trait UnsizedConstParamTy: StructuralPartialEq + Eq {} + +/// Derive macro generating an impl of the trait `ConstParamTy`. +#[cfg(not(bootstrap))] +#[cfg_attr(not(bootstrap), rustc_builtin_macro)] +#[cfg_attr(not(bootstrap), allow_internal_unstable(unsized_const_params))] +#[cfg_attr(not(bootstrap), unstable(feature = "unsized_const_params", issue = "95174"))] +pub macro UnsizedConstParamTy($item:item) { + /* compiler built-in */ +} + // FIXME(adt_const_params): handle `ty::FnDef`/`ty::Closure` marker_impls! { #[unstable(feature = "adt_const_params", issue = "95174")] - ConstParamTy for + ConstParamTy_ for usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, bool, char, (), - {T: ConstParamTy, const N: usize} [T; N], + {T: ConstParamTy_, const N: usize} [T; N], +} +#[cfg(bootstrap)] +marker_impls! { + #[unstable(feature = "adt_const_params", issue = "95174")] + ConstParamTy_ for + str, + {T: ConstParamTy_} [T], + {T: ConstParamTy_ + ?Sized} &T, } -#[unstable(feature = "adt_const_params", issue = "95174")] -#[rustc_reservation_impl = "types that are not `Sized` are not supported as the type of a const generic parameter"] -impl ConstParamTy for [T] {} - -#[unstable(feature = "adt_const_params", issue = "95174")] -#[rustc_reservation_impl = "types that are not `Sized` are not supported as the type of a const generic parameter"] -impl ConstParamTy for str {} +marker_impls! { + #[unstable(feature = "unsized_const_params", issue = "95174")] + UnsizedConstParamTy for + usize, u8, u16, u32, u64, u128, + isize, i8, i16, i32, i64, i128, + bool, + char, + (), + {T: UnsizedConstParamTy, const N: usize} [T; N], -#[unstable(feature = "adt_const_params", issue = "95174")] -#[rustc_reservation_impl = "references are not supported as the type of a const generic parameter"] -impl ConstParamTy for &T {} + str, + {T: UnsizedConstParamTy} [T], + {T: UnsizedConstParamTy + ?Sized} &T, +} /// A common trait implemented by all function pointers. #[unstable( diff --git a/core/src/mem/transmutability.rs b/core/src/mem/transmutability.rs index 827426b235839..ea73c5b80ba44 100644 --- a/core/src/mem/transmutability.rs +++ b/core/src/mem/transmutability.rs @@ -1,4 +1,4 @@ -use crate::marker::ConstParamTy; +use crate::marker::{ConstParamTy_, UnsizedConstParamTy}; /// Are values of a type transmutable into values of another type? /// @@ -39,7 +39,9 @@ pub struct Assume { } #[unstable(feature = "transmutability", issue = "99571")] -impl ConstParamTy for Assume {} +impl ConstParamTy_ for Assume {} +#[unstable(feature = "transmutability", issue = "99571")] +impl UnsizedConstParamTy for Assume {} impl Assume { /// Do not assume that *you* have ensured any safety properties are met. diff --git a/core/src/tuple.rs b/core/src/tuple.rs index 8e961d8adc372..bc376b13f64d9 100644 --- a/core/src/tuple.rs +++ b/core/src/tuple.rs @@ -1,8 +1,9 @@ // See core/src/primitive_docs.rs for documentation. use crate::cmp::Ordering::{self, *}; -use crate::marker::ConstParamTy; +use crate::marker::ConstParamTy_; use crate::marker::StructuralPartialEq; +use crate::marker::UnsizedConstParamTy; // Recursive macro for implementing n-ary tuple functions and operations // @@ -49,8 +50,15 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ - #[unstable(feature = "structural_match", issue = "31434")] - impl<$($T: ConstParamTy),+> ConstParamTy for ($($T,)+) + #[unstable(feature = "adt_const_params", issue = "95174")] + impl<$($T: ConstParamTy_),+> ConstParamTy_ for ($($T,)+) + {} + } + + maybe_tuple_doc! { + $($T)+ @ + #[unstable(feature = "unsized_const_params", issue = "95174")] + impl<$($T: UnsizedConstParamTy),+> UnsizedConstParamTy for ($($T,)+) {} } From c10a929ac5e1b7241fca72dddc616c19caa81020 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 18 Jul 2024 13:42:11 +0000 Subject: [PATCH 08/36] Safely enforce thread name requirements --- std/src/thread/mod.rs | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/std/src/thread/mod.rs b/std/src/thread/mod.rs index c8ee365392f85..87d5c2f742c41 100644 --- a/std/src/thread/mod.rs +++ b/std/src/thread/mod.rs @@ -161,7 +161,7 @@ mod tests; use crate::any::Any; use crate::cell::{OnceCell, UnsafeCell}; use crate::env; -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::fmt; use crate::io; use crate::marker::PhantomData; @@ -487,11 +487,7 @@ impl Builder { amt }); - let my_thread = name.map_or_else(Thread::new_unnamed, |name| unsafe { - Thread::new( - CString::new(name).expect("thread name may not contain interior null bytes"), - ) - }); + let my_thread = name.map_or_else(Thread::new_unnamed, |name| Thread::new(name.into())); let their_thread = my_thread.clone(); let my_packet: Arc> = Arc::new(Packet { @@ -1273,10 +1269,34 @@ impl ThreadId { /// The internal representation of a `Thread`'s name. enum ThreadName { Main, - Other(CString), + Other(ThreadNameString), Unnamed, } +// This module ensures private fields are kept private, which is necessary to enforce the safety requirements. +mod thread_name_string { + use crate::ffi::{CStr, CString}; + + /// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated. + pub(crate) struct ThreadNameString { + inner: CString, + } + impl core::ops::Deref for ThreadNameString { + type Target = CStr; + fn deref(&self) -> &CStr { + &self.inner + } + } + impl From for ThreadNameString { + fn from(s: String) -> Self { + Self { + inner: CString::new(s).expect("thread name may not contain interior null bytes"), + } + } + } +} +pub(crate) use thread_name_string::ThreadNameString; + /// The internal representation of a `Thread` handle struct Inner { name: ThreadName, // Guaranteed to be UTF-8 @@ -1316,10 +1336,7 @@ pub struct Thread { impl Thread { /// Used only internally to construct a thread object without spawning. - /// - /// # Safety - /// `name` must be valid UTF-8. - pub(crate) unsafe fn new(name: CString) -> Thread { + pub(crate) fn new(name: ThreadNameString) -> Thread { unsafe { Self::new_inner(ThreadName::Other(name)) } } From 16bce8a458aad26ac4921f6d6137a5b3bed43c83 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 18 Jul 2024 17:33:52 +0000 Subject: [PATCH 09/36] Make `Thread::new_inner` a safe function --- std/src/thread/mod.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/std/src/thread/mod.rs b/std/src/thread/mod.rs index 87d5c2f742c41..13b02cd8d3abe 100644 --- a/std/src/thread/mod.rs +++ b/std/src/thread/mod.rs @@ -1337,21 +1337,19 @@ pub struct Thread { impl Thread { /// Used only internally to construct a thread object without spawning. pub(crate) fn new(name: ThreadNameString) -> Thread { - unsafe { Self::new_inner(ThreadName::Other(name)) } + Self::new_inner(ThreadName::Other(name)) } pub(crate) fn new_unnamed() -> Thread { - unsafe { Self::new_inner(ThreadName::Unnamed) } + Self::new_inner(ThreadName::Unnamed) } // Used in runtime to construct main thread pub(crate) fn new_main() -> Thread { - unsafe { Self::new_inner(ThreadName::Main) } + Self::new_inner(ThreadName::Main) } - /// # Safety - /// If `name` is `ThreadName::Other(_)`, the contained string must be valid UTF-8. - unsafe fn new_inner(name: ThreadName) -> Thread { + 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. // From 68e23910a057f4996efbb4168479e749199f5be1 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 18 Jul 2024 18:10:36 +0000 Subject: [PATCH 10/36] Style change --- std/src/thread/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/std/src/thread/mod.rs b/std/src/thread/mod.rs index 13b02cd8d3abe..f3d20026c27b1 100644 --- a/std/src/thread/mod.rs +++ b/std/src/thread/mod.rs @@ -487,7 +487,7 @@ impl Builder { amt }); - let my_thread = name.map_or_else(Thread::new_unnamed, |name| Thread::new(name.into())); + let my_thread = name.map_or_else(Thread::new_unnamed, Thread::new); let their_thread = my_thread.clone(); let my_packet: Arc> = Arc::new(Packet { @@ -1336,8 +1336,8 @@ pub struct Thread { impl Thread { /// Used only internally to construct a thread object without spawning. - pub(crate) fn new(name: ThreadNameString) -> Thread { - Self::new_inner(ThreadName::Other(name)) + pub(crate) fn new(name: String) -> Thread { + Self::new_inner(ThreadName::Other(name.into())) } pub(crate) fn new_unnamed() -> Thread { From 83782615519bb4af7eb39ea3142a639205174e88 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 18 Jul 2024 18:13:11 +0000 Subject: [PATCH 11/36] Move ThreadName conversions to &cstr/&str --- std/src/thread/mod.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/std/src/thread/mod.rs b/std/src/thread/mod.rs index f3d20026c27b1..0c908a5adae4e 100644 --- a/std/src/thread/mod.rs +++ b/std/src/thread/mod.rs @@ -1275,7 +1275,9 @@ enum ThreadName { // This module ensures private fields are kept private, which is necessary to enforce the safety requirements. mod thread_name_string { + use super::ThreadName; use crate::ffi::{CStr, CString}; + use core::str; /// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated. pub(crate) struct ThreadNameString { @@ -1294,6 +1296,21 @@ mod thread_name_string { } } } + impl ThreadName { + pub fn as_cstr(&self) -> Option<&CStr> { + match self { + ThreadName::Main => Some(c"main"), + ThreadName::Other(other) => Some(other), + ThreadName::Unnamed => None, + } + } + + pub fn as_str(&self) -> Option<&str> { + // SAFETY: `as_cstr` can only return `Some` for a fixed CStr or a `ThreadNameString`, + // which is guaranteed to be UTF-8. + self.as_cstr().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) }) + } + } } pub(crate) use thread_name_string::ThreadNameString; @@ -1472,15 +1489,11 @@ impl Thread { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] pub fn name(&self) -> Option<&str> { - self.cname().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) }) + self.inner.name.as_str() } fn cname(&self) -> Option<&CStr> { - match &self.inner.name { - ThreadName::Main => Some(c"main"), - ThreadName::Other(other) => Some(&other), - ThreadName::Unnamed => None, - } + self.inner.name.as_cstr() } } From 9fb6e4958f77b771b87fa5e32d61777101b67734 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 16 Jul 2024 19:31:23 -0700 Subject: [PATCH 12/36] unix: Unsafe-wrap stack_overflow::signal_handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sometimes a safety comment is a prayer. avoid fuzzy provenance casts after deref. Co-authored-by: Jonas Böttiger --- std/src/sys/pal/unix/stack_overflow.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/std/src/sys/pal/unix/stack_overflow.rs b/std/src/sys/pal/unix/stack_overflow.rs index 2e5bd85327a19..0886624160c08 100644 --- a/std/src/sys/pal/unix/stack_overflow.rs +++ b/std/src/sys/pal/unix/stack_overflow.rs @@ -86,13 +86,18 @@ mod imp { // out many large systems and all implementations allow returning from a // signal handler to work. For a more detailed explanation see the // comments on #26458. + /// SIGSEGV/SIGBUS entry point + /// # Safety + /// Rust doesn't call this, it *gets called*. + #[forbid(unsafe_op_in_unsafe_fn)] unsafe extern "C" fn signal_handler( signum: libc::c_int, info: *mut libc::siginfo_t, _data: *mut libc::c_void, ) { let (start, end) = GUARD.get(); - let addr = (*info).si_addr() as usize; + // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`. + let addr = unsafe { (*info).si_addr().addr() }; // If the faulting address is within the guard page, then we print a // message saying so and abort. @@ -104,9 +109,11 @@ mod imp { rtabort!("stack overflow"); } else { // Unregister ourselves by reverting back to the default behavior. - let mut action: sigaction = mem::zeroed(); + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + let mut action: sigaction = unsafe { mem::zeroed() }; action.sa_sigaction = SIG_DFL; - sigaction(signum, &action, ptr::null_mut()); + // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction + unsafe { sigaction(signum, &action, ptr::null_mut()) }; // See comment above for why this function returns. } From 33a32f20bfdd70f2ce035d4bc9ca8977fd958646 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 16 Jul 2024 20:19:16 -0700 Subject: [PATCH 13/36] unix: lift init of sigaltstack before sigaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is technically "not necessary", as we will "just" segfault instead if we e.g. arrive inside the handler fn with the null altstack. However, it seems incorrect to go about this hoping that segfaulting is okay, seeing as how our purpose here is to mitigate stack overflow problems. Make sure NEED_ALTSTACK syncs with PAGE_SIZE when we do. Co-authored-by: Jonas Böttiger --- std/src/sys/pal/unix/stack_overflow.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/std/src/sys/pal/unix/stack_overflow.rs b/std/src/sys/pal/unix/stack_overflow.rs index 0886624160c08..160e58e16a5c4 100644 --- a/std/src/sys/pal/unix/stack_overflow.rs +++ b/std/src/sys/pal/unix/stack_overflow.rs @@ -123,28 +123,36 @@ mod imp { static MAIN_ALTSTACK: AtomicPtr = AtomicPtr::new(ptr::null_mut()); static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false); + /// # Safety + /// Must be called only once + #[forbid(unsafe_op_in_unsafe_fn)] 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); + let guard = unsafe { install_main_guard().unwrap_or(0..0) }; GUARD.set((guard.start, guard.end)); - let mut action: sigaction = mem::zeroed(); + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + let mut action: sigaction = unsafe { mem::zeroed() }; for &signal in &[SIGSEGV, SIGBUS] { - sigaction(signal, ptr::null_mut(), &mut action); + // SAFETY: just fetches the current signal handler into action + unsafe { sigaction(signal, ptr::null_mut(), &mut action) }; // Configure our signal handler if one is not already set. if action.sa_sigaction == SIG_DFL { + if !NEED_ALTSTACK.load(Ordering::Relaxed) { + // haven't set up our sigaltstack yet + NEED_ALTSTACK.store(true, Ordering::Release); + let handler = unsafe { make_handler(true) }; + MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); + mem::forget(handler); + } action.sa_flags = SA_SIGINFO | SA_ONSTACK; action.sa_sigaction = signal_handler as sighandler_t; - sigaction(signal, &action, ptr::null_mut()); - NEED_ALTSTACK.store(true, Ordering::Relaxed); + // SAFETY: only overriding signals if the default is set + unsafe { sigaction(signal, &action, ptr::null_mut()) }; } } - - let handler = make_handler(true); - MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); - mem::forget(handler); } pub unsafe fn cleanup() { From 72c7444faf0177277bac9a57fe38a3551d40cdd1 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 16 Jul 2024 21:07:44 -0700 Subject: [PATCH 14/36] unix: Unsafe-wrap stack_overflow::cleanup Editorialize on the wisdom of this as we do. --- std/src/sys/pal/unix/stack_overflow.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/std/src/sys/pal/unix/stack_overflow.rs b/std/src/sys/pal/unix/stack_overflow.rs index 160e58e16a5c4..e016f3b3ca4b3 100644 --- a/std/src/sys/pal/unix/stack_overflow.rs +++ b/std/src/sys/pal/unix/stack_overflow.rs @@ -155,8 +155,13 @@ mod imp { } } + /// # Safety + /// Must be called only once + #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn cleanup() { - drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed)); + // FIXME: I probably cause more bugs than I'm worth! + // see https://github.com/rust-lang/rust/issues/111272 + unsafe { drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed)) }; } unsafe fn get_stack() -> libc::stack_t { From 9e11e01d38fc4e492ad05959aadd9b6b291673fb Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 16 Jul 2024 21:08:06 -0700 Subject: [PATCH 15/36] unix: Unsafe-wrap stack_overflow::{drop,make}_handler Note that current_guard is probably not unsafe for future work. --- std/src/sys/pal/unix/stack_overflow.rs | 37 +++++++++++++++++++------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/std/src/sys/pal/unix/stack_overflow.rs b/std/src/sys/pal/unix/stack_overflow.rs index e016f3b3ca4b3..3944e4e1b48a2 100644 --- a/std/src/sys/pal/unix/stack_overflow.rs +++ b/std/src/sys/pal/unix/stack_overflow.rs @@ -206,6 +206,9 @@ mod imp { libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size } } + /// # Safety + /// Mutates the alternate signal stack + #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn make_handler(main_thread: bool) -> Handler { if !NEED_ALTSTACK.load(Ordering::Relaxed) { return Handler::null(); @@ -213,27 +216,38 @@ mod imp { if !main_thread { // Always write to GUARD to ensure the TLS variable is allocated. - let guard = current_guard().unwrap_or(0..0); + let guard = unsafe { current_guard() }.unwrap_or(0..0); GUARD.set((guard.start, guard.end)); } - let mut stack = mem::zeroed(); - sigaltstack(ptr::null(), &mut stack); + // SAFETY: assuming stack_t is zero-initializable + let mut stack = unsafe { mem::zeroed() }; + // SAFETY: reads current stack_t into stack + unsafe { sigaltstack(ptr::null(), &mut stack) }; // Configure alternate signal stack, if one is not already set. if stack.ss_flags & SS_DISABLE != 0 { - stack = get_stack(); - sigaltstack(&stack, ptr::null_mut()); + // SAFETY: We warned our caller this would happen! + unsafe { + stack = get_stack(); + sigaltstack(&stack, ptr::null_mut()); + } Handler { data: stack.ss_sp as *mut libc::c_void } } else { Handler::null() } } + /// # Safety + /// Must be called + /// - only with our handler or nullptr + /// - only when done with our altstack + /// This disables the alternate signal stack! + #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn drop_handler(data: *mut libc::c_void) { if !data.is_null() { let sigstack_size = sigstack_size(); let page_size = PAGE_SIZE.load(Ordering::Relaxed); - let stack = libc::stack_t { + let disabling_stack = libc::stack_t { ss_sp: ptr::null_mut(), ss_flags: SS_DISABLE, // Workaround for bug in macOS implementation of sigaltstack @@ -242,10 +256,11 @@ mod imp { // both ss_sp and ss_size should be ignored in this case. ss_size: sigstack_size, }; - sigaltstack(&stack, ptr::null_mut()); - // We know from `get_stackp` that the alternate stack we installed is part of a mapping - // that started one page earlier, so walk back a page and unmap from there. - munmap(data.sub(page_size), sigstack_size + page_size); + // SAFETY: we warned the caller this disables the alternate signal stack! + unsafe { sigaltstack(&disabling_stack, ptr::null_mut()) }; + // SAFETY: We know from `get_stackp` that the alternate stack we installed is part of + // a mapping that started one page earlier, so walk back a page and unmap from there. + unsafe { munmap(data.sub(page_size), sigstack_size + page_size) }; } } @@ -446,6 +461,7 @@ mod imp { } #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] + // FIXME: I am probably not unsafe. unsafe fn current_guard() -> Option> { let stackptr = get_stack_start()?; let stackaddr = stackptr.addr(); @@ -460,6 +476,7 @@ mod imp { target_os = "netbsd", target_os = "l4re" ))] + // FIXME: I am probably not unsafe. unsafe fn current_guard() -> Option> { let mut ret = None; let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); From 489f1ef8747c915034cc5f82cd574581669ef2c3 Mon Sep 17 00:00:00 2001 From: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:24:40 -0700 Subject: [PATCH 16/36] unix: acquire-load NEED_ALTSTACK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jonas Böttiger --- std/src/sys/pal/unix/stack_overflow.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/src/sys/pal/unix/stack_overflow.rs b/std/src/sys/pal/unix/stack_overflow.rs index 3944e4e1b48a2..047edebb8617b 100644 --- a/std/src/sys/pal/unix/stack_overflow.rs +++ b/std/src/sys/pal/unix/stack_overflow.rs @@ -210,7 +210,7 @@ mod imp { /// Mutates the alternate signal stack #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn make_handler(main_thread: bool) -> Handler { - if !NEED_ALTSTACK.load(Ordering::Relaxed) { + if !NEED_ALTSTACK.load(Ordering::Acquire) { return Handler::null(); } From e6eeb4ee29541ef62f8eafc40c177c89619643ac Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Wed, 27 Mar 2024 01:07:39 +0530 Subject: [PATCH 17/36] uefi: Add process Signed-off-by: Ayush Singh --- std/src/sys/pal/uefi/helpers.rs | 72 ++++++- std/src/sys/pal/uefi/mod.rs | 1 - std/src/sys/pal/uefi/process.rs | 328 ++++++++++++++++++++++++++++++++ 3 files changed, 399 insertions(+), 2 deletions(-) create mode 100644 std/src/sys/pal/uefi/process.rs diff --git a/std/src/sys/pal/uefi/helpers.rs b/std/src/sys/pal/uefi/helpers.rs index 23aa4da14a763..97dd90716bbb0 100644 --- a/std/src/sys/pal/uefi/helpers.rs +++ b/std/src/sys/pal/uefi/helpers.rs @@ -12,7 +12,7 @@ use r_efi::efi::{self, Guid}; use r_efi::protocols::{device_path, device_path_to_text}; -use crate::ffi::OsString; +use crate::ffi::{OsString, OsStr}; use crate::io::{self, const_io_error}; use crate::mem::{size_of, MaybeUninit}; use crate::os::uefi::{self, env::boot_services, ffi::OsStringExt}; @@ -221,3 +221,73 @@ pub(crate) fn runtime_services() -> Option> let runtime_services = unsafe { (*system_table.as_ptr()).runtime_services }; NonNull::new(runtime_services) } + +pub(crate) struct DevicePath(NonNull); + +impl DevicePath { + pub(crate) fn from_text(p: &OsStr) -> io::Result { + fn inner( + p: &OsStr, + protocol: NonNull, + ) -> io::Result { + let path_vec = p.encode_wide().chain(Some(0)).collect::>(); + let path = + unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) }; + + NonNull::new(path).map(DevicePath).ok_or_else(|| { + const_io_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path") + }) + } + + static LAST_VALID_HANDLE: AtomicPtr = + AtomicPtr::new(crate::ptr::null_mut()); + + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { + if let Ok(protocol) = open_protocol::( + handle, + r_efi::protocols::device_path_from_text::PROTOCOL_GUID, + ) { + return inner(p, protocol); + } + } + + let handles = locate_handles(r_efi::protocols::device_path_from_text::PROTOCOL_GUID)?; + for handle in handles { + if let Ok(protocol) = open_protocol::( + handle, + r_efi::protocols::device_path_from_text::PROTOCOL_GUID, + ) { + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); + return inner(p, protocol); + } + } + + io::Result::Err(const_io_error!( + io::ErrorKind::NotFound, + "DevicePathFromText Protocol not found" + )) + } +} + +impl AsRef for DevicePath { + fn as_ref(&self) -> &r_efi::protocols::device_path::Protocol { + unsafe { self.0.as_ref() } + } +} + +impl AsMut for DevicePath { + fn as_mut(&mut self) -> &mut r_efi::protocols::device_path::Protocol { + unsafe { self.0.as_mut() } + } +} + +impl Drop for DevicePath { + fn drop(&mut self) { + if let Some(bt) = boot_services() { + let bt: NonNull = bt.cast(); + unsafe { + ((*bt.as_ptr()).free_pool)(self.0.as_ptr() as *mut crate::ffi::c_void); + } + } + } +} diff --git a/std/src/sys/pal/uefi/mod.rs b/std/src/sys/pal/uefi/mod.rs index 4d50d9e8c3d9c..c54e9477bfc1f 100644 --- a/std/src/sys/pal/uefi/mod.rs +++ b/std/src/sys/pal/uefi/mod.rs @@ -25,7 +25,6 @@ pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/process.rs"] pub mod process; pub mod stdio; pub mod thread; diff --git a/std/src/sys/pal/uefi/process.rs b/std/src/sys/pal/uefi/process.rs new file mode 100644 index 0000000000000..e85fc209c0e18 --- /dev/null +++ b/std/src/sys/pal/uefi/process.rs @@ -0,0 +1,328 @@ +use crate::ffi::OsStr; +use crate::ffi::OsString; +use crate::fmt; +use crate::io; +use crate::marker::PhantomData; +use crate::num::NonZero; +use crate::num::NonZeroI32; +use crate::path::Path; +use crate::sys::fs::File; +use crate::sys::pipe::AnonPipe; +use crate::sys::unsupported; +use crate::sys_common::process::{CommandEnv, CommandEnvs}; + +pub use crate::ffi::OsString as EnvKey; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + prog: OsString, +} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + +// 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. +pub enum Stdio { + Inherit, + Null, + MakePipe, +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + Command { prog: program.to_os_string() } + } + + pub fn arg(&mut self, _arg: &OsStr) { + panic!("unsupported") + } + + pub fn env_mut(&mut self) -> &mut CommandEnv { + panic!("unsupported") + } + + pub fn cwd(&mut self, _dir: &OsStr) { + panic!("unsupported") + } + + pub fn stdin(&mut self, _stdin: Stdio) { + panic!("unsupported") + } + + pub fn stdout(&mut self, _stdout: Stdio) { + panic!("unsupported") + } + + pub fn stderr(&mut self, _stderr: Stdio) { + panic!("unsupported") + } + + pub fn get_program(&self) -> &OsStr { + panic!("unsupported") + } + + pub fn get_args(&self) -> CommandArgs<'_> { + panic!("unsupported") + } + + pub fn get_envs(&self) -> CommandEnvs<'_> { + panic!("unsupported") + } + + pub fn get_current_dir(&self) -> Option<&Path> { + None + } + + pub fn spawn( + &mut self, + _default: Stdio, + _needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + unsupported() + } + + pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { + let cmd = uefi_command_internal::Command::load_image(&self.prog)?; + let stat = cmd.start_image()?; + Ok((ExitStatus(stat), Vec::new(), Vec::new())) + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +impl From for Stdio { + fn from(_file: File) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +impl fmt::Debug for Command { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + Ok(()) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[non_exhaustive] +pub struct ExitStatus(r_efi::efi::Status); + +impl ExitStatus { + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + if self.0 == r_efi::efi::Status::SUCCESS { Ok(()) } else { Err(ExitStatusError(self.0)) } + } + + pub fn code(&self) -> Option { + Some(self.0.as_usize() as i32) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let err_str = super::os::error_string(self.0.as_usize()); + write!(f, "{}", err_str) + } +} + +impl Default for ExitStatus { + fn default() -> Self { + ExitStatus(r_efi::efi::Status::SUCCESS) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct ExitStatusError(r_efi::efi::Status); + +impl fmt::Debug for ExitStatusError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let err_str = super::os::error_string(self.0.as_usize()); + write!(f, "{}", err_str) + } +} + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option> { + NonZeroI32::new(self.0.as_usize() as i32) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(bool); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(false); + pub const FAILURE: ExitCode = ExitCode(true); + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + +impl From for ExitCode { + fn from(code: u8) -> Self { + match code { + 0 => Self::SUCCESS, + 1..=255 => Self::FAILURE, + } + } +} + +pub struct Process(!); + +impl Process { + pub fn id(&self) -> u32 { + self.0 + } + + pub fn kill(&mut self) -> io::Result<()> { + self.0 + } + + pub fn wait(&mut self) -> io::Result { + self.0 + } + + pub fn try_wait(&mut self) -> io::Result> { + self.0 + } +} + +pub struct CommandArgs<'a> { + _p: PhantomData<&'a ()>, +} + +impl<'a> Iterator for CommandArgs<'a> { + type Item = &'a OsStr; + fn next(&mut self) -> Option<&'a OsStr> { + None + } + fn size_hint(&self) -> (usize, Option) { + (0, Some(0)) + } +} + +impl<'a> ExactSizeIterator for CommandArgs<'a> {} + +impl<'a> fmt::Debug for CommandArgs<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().finish() + } +} + +mod uefi_command_internal { + use super::super::helpers; + use crate::ffi::OsStr; + use crate::io::{self, const_io_error}; + use crate::mem::MaybeUninit; + use crate::os::uefi::env::{boot_services, image_handle}; + use crate::ptr::NonNull; + + pub struct Command { + handle: NonNull, + } + + impl Command { + const fn new(handle: NonNull) -> Self { + Self { handle } + } + + pub fn load_image(p: &OsStr) -> io::Result { + let mut path = helpers::DevicePath::from_text(p)?; + let boot_services: NonNull = boot_services() + .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))? + .cast(); + let mut child_handle: MaybeUninit = MaybeUninit::uninit(); + let image_handle = image_handle(); + + let r = unsafe { + ((*boot_services.as_ptr()).load_image)( + r_efi::efi::Boolean::FALSE, + image_handle.as_ptr(), + path.as_mut(), + crate::ptr::null_mut(), + 0, + child_handle.as_mut_ptr(), + ) + }; + + if r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + let child_handle = unsafe { child_handle.assume_init() }; + let child_handle = NonNull::new(child_handle).unwrap(); + Ok(Self::new(child_handle)) + } + } + + pub fn start_image(&self) -> io::Result { + let boot_services: NonNull = boot_services() + .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))? + .cast(); + let mut exit_data_size: MaybeUninit = MaybeUninit::uninit(); + let mut exit_data: MaybeUninit<*mut u16> = MaybeUninit::uninit(); + + let r = unsafe { + ((*boot_services.as_ptr()).start_image)( + self.handle.as_ptr(), + exit_data_size.as_mut_ptr(), + exit_data.as_mut_ptr(), + ) + }; + + // Drop exitdata + unsafe { + exit_data_size.assume_init_drop(); + exit_data.assume_init_drop(); + } + + Ok(r) + } + } + + impl Drop for Command { + fn drop(&mut self) { + if let Some(bt) = boot_services() { + let bt: NonNull = bt.cast(); + unsafe { + ((*bt.as_ptr()).unload_image)(self.handle.as_ptr()); + } + } + } + } +} From b712e740ca4f9598fa2897074fb41dbb2eb68f1f Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Fri, 29 Mar 2024 17:13:16 +0530 Subject: [PATCH 18/36] uefi: process: Add support to capture stdout Use a custom simple_text_output protocol to capture output. Signed-off-by: Ayush Singh --- std/src/sys/pal/uefi/helpers.rs | 76 ++++++++++++- std/src/sys/pal/uefi/process.rs | 190 +++++++++++++++++++++++++++++++- 2 files changed, 258 insertions(+), 8 deletions(-) diff --git a/std/src/sys/pal/uefi/helpers.rs b/std/src/sys/pal/uefi/helpers.rs index 97dd90716bbb0..c2419bbb58573 100644 --- a/std/src/sys/pal/uefi/helpers.rs +++ b/std/src/sys/pal/uefi/helpers.rs @@ -12,10 +12,10 @@ use r_efi::efi::{self, Guid}; use r_efi::protocols::{device_path, device_path_to_text}; -use crate::ffi::{OsString, OsStr}; +use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_io_error}; use crate::mem::{size_of, MaybeUninit}; -use crate::os::uefi::{self, env::boot_services, ffi::OsStringExt}; +use crate::os::uefi::{self, env::boot_services, ffi::OsStrExt, ffi::OsStringExt}; use crate::ptr::NonNull; use crate::slice; use crate::sync::atomic::{AtomicPtr, Ordering}; @@ -291,3 +291,75 @@ impl Drop for DevicePath { } } } + +pub(crate) struct Protocol { + guid: r_efi::efi::Guid, + handle: NonNull, + protocol: Box, +} + +impl Protocol { + const fn new( + guid: r_efi::efi::Guid, + handle: NonNull, + protocol: Box, + ) -> Self { + Self { guid, handle, protocol } + } + + pub(crate) fn create(protocol: T, mut guid: r_efi::efi::Guid) -> io::Result { + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let mut protocol = Box::new(protocol); + let mut handle: r_efi::efi::Handle = crate::ptr::null_mut(); + + let r = unsafe { + ((*boot_services.as_ptr()).install_protocol_interface)( + &mut handle, + &mut guid, + r_efi::efi::NATIVE_INTERFACE, + protocol.as_mut() as *mut T as *mut crate::ffi::c_void, + ) + }; + + if r.is_error() { + return Err(crate::io::Error::from_raw_os_error(r.as_usize())); + }; + + let handle = NonNull::new(handle) + .ok_or(io::const_io_error!(io::ErrorKind::Uncategorized, "found null handle"))?; + + Ok(Self::new(guid, handle, protocol)) + } + + pub(crate) fn handle(&self) -> NonNull { + self.handle + } +} + +impl Drop for Protocol { + fn drop(&mut self) { + if let Some(bt) = boot_services() { + let bt: NonNull = bt.cast(); + unsafe { + ((*bt.as_ptr()).uninstall_protocol_interface)( + self.handle.as_ptr(), + &mut self.guid, + self.protocol.as_mut() as *mut T as *mut crate::ffi::c_void, + ) + }; + } + } +} + +impl AsRef for Protocol { + fn as_ref(&self) -> &T { + &self.protocol + } +} + +impl AsMut for Protocol { + fn as_mut(&mut self) -> &mut T { + &mut self.protocol + } +} diff --git a/std/src/sys/pal/uefi/process.rs b/std/src/sys/pal/uefi/process.rs index e85fc209c0e18..7abacc7c12e96 100644 --- a/std/src/sys/pal/uefi/process.rs +++ b/std/src/sys/pal/uefi/process.rs @@ -91,9 +91,11 @@ impl Command { } pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - let cmd = uefi_command_internal::Command::load_image(&self.prog)?; + let mut cmd = uefi_command_internal::Command::load_image(&self.prog)?; + cmd.stdout_init()?; let stat = cmd.start_image()?; - Ok((ExitStatus(stat), Vec::new(), Vec::new())) + let stdout = cmd.stdout()?; + Ok((ExitStatus(stat), stdout, Vec::new())) } } @@ -246,20 +248,30 @@ impl<'a> fmt::Debug for CommandArgs<'a> { } mod uefi_command_internal { + use r_efi::protocols::{loaded_image, simple_text_output}; + use super::super::helpers; - use crate::ffi::OsStr; + use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_io_error}; use crate::mem::MaybeUninit; use crate::os::uefi::env::{boot_services, image_handle}; + use crate::os::uefi::ffi::OsStringExt; use crate::ptr::NonNull; + use crate::slice; + use crate::sys_common::wstr::WStrUnits; pub struct Command { handle: NonNull, + stdout: Option>, + st: Box, } impl Command { - const fn new(handle: NonNull) -> Self { - Self { handle } + const fn new( + handle: NonNull, + st: Box, + ) -> Self { + Self { handle, stdout: None, st } } pub fn load_image(p: &OsStr) -> io::Result { @@ -286,7 +298,17 @@ mod uefi_command_internal { } else { let child_handle = unsafe { child_handle.assume_init() }; let child_handle = NonNull::new(child_handle).unwrap(); - Ok(Self::new(child_handle)) + + let loaded_image: NonNull = + helpers::open_protocol(child_handle, loaded_image::PROTOCOL_GUID).unwrap(); + let mut st: Box = + Box::new(unsafe { crate::ptr::read((*loaded_image.as_ptr()).system_table) }); + + unsafe { + (*loaded_image.as_ptr()).system_table = st.as_mut(); + } + + Ok(Self::new(child_handle, st)) } } @@ -313,6 +335,32 @@ mod uefi_command_internal { Ok(r) } + + pub fn stdout_init(&mut self) -> io::Result<()> { + let mut protocol = + helpers::Protocol::create(PipeProtocol::new(), simple_text_output::PROTOCOL_GUID)?; + + self.st.console_out_handle = protocol.handle().as_ptr(); + self.st.con_out = + protocol.as_mut() as *mut PipeProtocol as *mut simple_text_output::Protocol; + + self.stdout = Some(protocol); + + Ok(()) + } + + pub fn stdout(&self) -> io::Result> { + if let Some(stdout) = &self.stdout { + stdout + .as_ref() + .utf8() + .into_string() + .map_err(|_| const_io_error!(io::ErrorKind::Other, "utf8 conversion failed")) + .map(Into::into) + } else { + Err(const_io_error!(io::ErrorKind::NotFound, "stdout not found")) + } + } } impl Drop for Command { @@ -325,4 +373,134 @@ mod uefi_command_internal { } } } + + #[repr(C)] + struct PipeProtocol { + reset: simple_text_output::ProtocolReset, + output_string: simple_text_output::ProtocolOutputString, + test_string: simple_text_output::ProtocolTestString, + query_mode: simple_text_output::ProtocolQueryMode, + set_mode: simple_text_output::ProtocolSetMode, + set_attribute: simple_text_output::ProtocolSetAttribute, + clear_screen: simple_text_output::ProtocolClearScreen, + set_cursor_position: simple_text_output::ProtocolSetCursorPosition, + enable_cursor: simple_text_output::ProtocolEnableCursor, + mode: *mut simple_text_output::Mode, + _mode: Box, + _buffer: Vec, + } + + impl PipeProtocol { + fn new() -> Self { + let mut mode = Box::new(simple_text_output::Mode { + max_mode: 0, + mode: 0, + attribute: 0, + cursor_column: 0, + cursor_row: 0, + cursor_visible: r_efi::efi::Boolean::FALSE, + }); + Self { + reset: Self::reset, + output_string: Self::output_string, + test_string: Self::test_string, + query_mode: Self::query_mode, + set_mode: Self::set_mode, + set_attribute: Self::set_attribute, + clear_screen: Self::clear_screen, + set_cursor_position: Self::set_cursor_position, + enable_cursor: Self::enable_cursor, + mode: mode.as_mut(), + _mode: mode, + _buffer: Vec::new(), + } + } + + fn utf8(&self) -> OsString { + OsString::from_wide(&self._buffer) + } + + extern "efiapi" fn reset( + proto: *mut simple_text_output::Protocol, + _: r_efi::efi::Boolean, + ) -> r_efi::efi::Status { + let proto: *mut PipeProtocol = proto.cast(); + unsafe { + (*proto)._buffer.clear(); + } + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn output_string( + proto: *mut simple_text_output::Protocol, + buf: *mut r_efi::efi::Char16, + ) -> r_efi::efi::Status { + let proto: *mut PipeProtocol = proto.cast(); + let buf_len = unsafe { + if let Some(x) = WStrUnits::new(buf) { + x.count() + } else { + return r_efi::efi::Status::INVALID_PARAMETER; + } + }; + let buf_slice = unsafe { slice::from_raw_parts(buf, buf_len) }; + + unsafe { + (*proto)._buffer.extend_from_slice(buf_slice); + }; + + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn test_string( + _: *mut simple_text_output::Protocol, + _: *mut r_efi::efi::Char16, + ) -> r_efi::efi::Status { + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn query_mode( + _: *mut simple_text_output::Protocol, + _: usize, + _: *mut usize, + _: *mut usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn set_mode( + _: *mut simple_text_output::Protocol, + _: usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn set_attribute( + _: *mut simple_text_output::Protocol, + _: usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn clear_screen( + _: *mut simple_text_output::Protocol, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn set_cursor_position( + _: *mut simple_text_output::Protocol, + _: usize, + _: usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn enable_cursor( + _: *mut simple_text_output::Protocol, + _: r_efi::efi::Boolean, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + } } From 36a0e1e01c838e1e47a23ca4233b34ed62e7259e Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Fri, 29 Mar 2024 17:20:50 +0530 Subject: [PATCH 19/36] uefi: process: Add stderr support Implement stderr support in similar fashion. Signed-off-by: Ayush Singh --- std/src/sys/pal/uefi/process.rs | 36 +++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/std/src/sys/pal/uefi/process.rs b/std/src/sys/pal/uefi/process.rs index 7abacc7c12e96..06ce542b0be1f 100644 --- a/std/src/sys/pal/uefi/process.rs +++ b/std/src/sys/pal/uefi/process.rs @@ -92,10 +92,15 @@ impl Command { pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { let mut cmd = uefi_command_internal::Command::load_image(&self.prog)?; + cmd.stdout_init()?; + cmd.stderr_init()?; + let stat = cmd.start_image()?; let stdout = cmd.stdout()?; - Ok((ExitStatus(stat), stdout, Vec::new())) + let stderr = cmd.stderr()?; + + Ok((ExitStatus(stat), stdout, stderr)) } } @@ -263,6 +268,7 @@ mod uefi_command_internal { pub struct Command { handle: NonNull, stdout: Option>, + stderr: Option>, st: Box, } @@ -271,7 +277,7 @@ mod uefi_command_internal { handle: NonNull, st: Box, ) -> Self { - Self { handle, stdout: None, st } + Self { handle, stdout: None, stderr: None, st } } pub fn load_image(p: &OsStr) -> io::Result { @@ -349,6 +355,19 @@ mod uefi_command_internal { Ok(()) } + pub fn stderr_init(&mut self) -> io::Result<()> { + let mut protocol = + helpers::Protocol::create(PipeProtocol::new(), simple_text_output::PROTOCOL_GUID)?; + + self.st.standard_error_handle = protocol.handle().as_ptr(); + self.st.std_err = + protocol.as_mut() as *mut PipeProtocol as *mut simple_text_output::Protocol; + + self.stderr = Some(protocol); + + Ok(()) + } + pub fn stdout(&self) -> io::Result> { if let Some(stdout) = &self.stdout { stdout @@ -361,6 +380,19 @@ mod uefi_command_internal { Err(const_io_error!(io::ErrorKind::NotFound, "stdout not found")) } } + + pub fn stderr(&self) -> io::Result> { + if let Some(stderr) = &self.stderr { + stderr + .as_ref() + .utf8() + .into_string() + .map_err(|_| const_io_error!(io::ErrorKind::Other, "utf8 conversion failed")) + .map(Into::into) + } else { + Err(const_io_error!(io::ErrorKind::NotFound, "stdout not found")) + } + } } impl Drop for Command { From 24a95828625dc6c7102b33d3685050d68593472e Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Fri, 29 Mar 2024 18:36:52 +0530 Subject: [PATCH 20/36] uefi: process: Add null protocol Signed-off-by: Ayush Singh --- std/src/sys/pal/uefi/process.rs | 138 +++++++++++++++++++++++--------- 1 file changed, 100 insertions(+), 38 deletions(-) diff --git a/std/src/sys/pal/uefi/process.rs b/std/src/sys/pal/uefi/process.rs index 06ce542b0be1f..fc96f382650e5 100644 --- a/std/src/sys/pal/uefi/process.rs +++ b/std/src/sys/pal/uefi/process.rs @@ -1,3 +1,5 @@ +use r_efi::protocols::simple_text_output; + use crate::ffi::OsStr; use crate::ffi::OsString; use crate::fmt; @@ -13,12 +15,16 @@ use crate::sys_common::process::{CommandEnv, CommandEnvs}; pub use crate::ffi::OsString as EnvKey; +use super::helpers; + //////////////////////////////////////////////////////////////////////////////// // Command //////////////////////////////////////////////////////////////////////////////// pub struct Command { prog: OsString, + stdout: Option, + stderr: Option, } // passed back to std::process with the pipes connected to the child, if any @@ -39,7 +45,7 @@ pub enum Stdio { impl Command { pub fn new(program: &OsStr) -> Command { - Command { prog: program.to_os_string() } + Command { prog: program.to_os_string(), stdout: None, stderr: None } } pub fn arg(&mut self, _arg: &OsStr) { @@ -58,12 +64,20 @@ impl Command { panic!("unsupported") } - pub fn stdout(&mut self, _stdout: Stdio) { - panic!("unsupported") + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = match stdout { + Stdio::MakePipe => Some(uefi_command_internal::PipeProtocol::new()), + Stdio::Null => Some(uefi_command_internal::PipeProtocol::null()), + _ => None, + }; } - pub fn stderr(&mut self, _stderr: Stdio) { - panic!("unsupported") + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = match stderr { + Stdio::MakePipe => Some(uefi_command_internal::PipeProtocol::new()), + Stdio::Null => Some(uefi_command_internal::PipeProtocol::null()), + _ => None, + }; } pub fn get_program(&self) -> &OsStr { @@ -93,8 +107,26 @@ impl Command { pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { let mut cmd = uefi_command_internal::Command::load_image(&self.prog)?; - cmd.stdout_init()?; - cmd.stderr_init()?; + let stdout: helpers::Protocol = + match self.stdout.take() { + Some(s) => helpers::Protocol::create(s, simple_text_output::PROTOCOL_GUID), + None => helpers::Protocol::create( + uefi_command_internal::PipeProtocol::new(), + simple_text_output::PROTOCOL_GUID, + ), + }?; + + let stderr: helpers::Protocol = + match self.stderr.take() { + Some(s) => helpers::Protocol::create(s, simple_text_output::PROTOCOL_GUID), + None => helpers::Protocol::create( + uefi_command_internal::PipeProtocol::new(), + simple_text_output::PROTOCOL_GUID, + ), + }?; + + cmd.stdout_init(stdout)?; + cmd.stderr_init(stderr)?; let stat = cmd.start_image()?; let stdout = cmd.stdout()?; @@ -342,10 +374,10 @@ mod uefi_command_internal { Ok(r) } - pub fn stdout_init(&mut self) -> io::Result<()> { - let mut protocol = - helpers::Protocol::create(PipeProtocol::new(), simple_text_output::PROTOCOL_GUID)?; - + pub fn stdout_init( + &mut self, + mut protocol: helpers::Protocol, + ) -> io::Result<()> { self.st.console_out_handle = protocol.handle().as_ptr(); self.st.con_out = protocol.as_mut() as *mut PipeProtocol as *mut simple_text_output::Protocol; @@ -355,10 +387,10 @@ mod uefi_command_internal { Ok(()) } - pub fn stderr_init(&mut self) -> io::Result<()> { - let mut protocol = - helpers::Protocol::create(PipeProtocol::new(), simple_text_output::PROTOCOL_GUID)?; - + pub fn stderr_init( + &mut self, + mut protocol: helpers::Protocol, + ) -> io::Result<()> { self.st.standard_error_handle = protocol.handle().as_ptr(); self.st.std_err = protocol.as_mut() as *mut PipeProtocol as *mut simple_text_output::Protocol; @@ -368,29 +400,17 @@ mod uefi_command_internal { Ok(()) } - pub fn stdout(&self) -> io::Result> { - if let Some(stdout) = &self.stdout { - stdout - .as_ref() - .utf8() - .into_string() - .map_err(|_| const_io_error!(io::ErrorKind::Other, "utf8 conversion failed")) - .map(Into::into) - } else { - Err(const_io_error!(io::ErrorKind::NotFound, "stdout not found")) + pub fn stderr(&self) -> io::Result> { + match &self.stderr { + Some(stderr) => stderr.as_ref().utf8(), + None => Ok(Vec::new()), } } - pub fn stderr(&self) -> io::Result> { - if let Some(stderr) = &self.stderr { - stderr - .as_ref() - .utf8() - .into_string() - .map_err(|_| const_io_error!(io::ErrorKind::Other, "utf8 conversion failed")) - .map(Into::into) - } else { - Err(const_io_error!(io::ErrorKind::NotFound, "stdout not found")) + pub fn stdout(&self) -> io::Result> { + match &self.stdout { + Some(stdout) => stdout.as_ref().utf8(), + None => Ok(Vec::new()), } } } @@ -407,7 +427,7 @@ mod uefi_command_internal { } #[repr(C)] - struct PipeProtocol { + pub struct PipeProtocol { reset: simple_text_output::ProtocolReset, output_string: simple_text_output::ProtocolOutputString, test_string: simple_text_output::ProtocolTestString, @@ -423,7 +443,7 @@ mod uefi_command_internal { } impl PipeProtocol { - fn new() -> Self { + pub fn new() -> Self { let mut mode = Box::new(simple_text_output::Mode { max_mode: 0, mode: 0, @@ -448,8 +468,36 @@ mod uefi_command_internal { } } - fn utf8(&self) -> OsString { + pub fn null() -> Self { + let mut mode = Box::new(simple_text_output::Mode { + max_mode: 0, + mode: 0, + attribute: 0, + cursor_column: 0, + cursor_row: 0, + cursor_visible: r_efi::efi::Boolean::FALSE, + }); + Self { + reset: Self::reset_null, + output_string: Self::output_string_null, + test_string: Self::test_string, + query_mode: Self::query_mode, + set_mode: Self::set_mode, + set_attribute: Self::set_attribute, + clear_screen: Self::clear_screen, + set_cursor_position: Self::set_cursor_position, + enable_cursor: Self::enable_cursor, + mode: mode.as_mut(), + _mode: mode, + _buffer: Vec::new(), + } + } + + pub fn utf8(&self) -> io::Result> { OsString::from_wide(&self._buffer) + .into_string() + .map(Into::into) + .map_err(|_| const_io_error!(io::ErrorKind::Other, "utf8 conversion failed")) } extern "efiapi" fn reset( @@ -463,6 +511,13 @@ mod uefi_command_internal { r_efi::efi::Status::SUCCESS } + extern "efiapi" fn reset_null( + _: *mut simple_text_output::Protocol, + _: r_efi::efi::Boolean, + ) -> r_efi::efi::Status { + r_efi::efi::Status::SUCCESS + } + extern "efiapi" fn output_string( proto: *mut simple_text_output::Protocol, buf: *mut r_efi::efi::Char16, @@ -484,6 +539,13 @@ mod uefi_command_internal { r_efi::efi::Status::SUCCESS } + extern "efiapi" fn output_string_null( + _: *mut simple_text_output::Protocol, + _: *mut r_efi::efi::Char16, + ) -> r_efi::efi::Status { + r_efi::efi::Status::SUCCESS + } + extern "efiapi" fn test_string( _: *mut simple_text_output::Protocol, _: *mut r_efi::efi::Char16, From 1991fe38c57988cc74d02dcbcf18fc4677c81a2e Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Fri, 29 Mar 2024 19:07:46 +0530 Subject: [PATCH 21/36] uefi: process Implement inherit Only tested in 2 levels right now. Need args support for 3 levels Signed-off-by: Ayush Singh --- std/src/sys/pal/uefi/process.rs | 57 +++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/std/src/sys/pal/uefi/process.rs b/std/src/sys/pal/uefi/process.rs index fc96f382650e5..ed25ad81bbdc9 100644 --- a/std/src/sys/pal/uefi/process.rs +++ b/std/src/sys/pal/uefi/process.rs @@ -23,8 +23,8 @@ use super::helpers; pub struct Command { prog: OsString, - stdout: Option, - stderr: Option, + stdout: Option, + stderr: Option, } // passed back to std::process with the pipes connected to the child, if any @@ -65,19 +65,11 @@ impl Command { } pub fn stdout(&mut self, stdout: Stdio) { - self.stdout = match stdout { - Stdio::MakePipe => Some(uefi_command_internal::PipeProtocol::new()), - Stdio::Null => Some(uefi_command_internal::PipeProtocol::null()), - _ => None, - }; + self.stdout = Some(stdout); } pub fn stderr(&mut self, stderr: Stdio) { - self.stderr = match stderr { - Stdio::MakePipe => Some(uefi_command_internal::PipeProtocol::new()), - Stdio::Null => Some(uefi_command_internal::PipeProtocol::null()), - _ => None, - }; + self.stderr = Some(stderr); } pub fn get_program(&self) -> &OsStr { @@ -104,31 +96,56 @@ impl Command { unsupported() } + fn create_pipe( + s: Stdio, + ) -> io::Result>> { + match s { + Stdio::MakePipe => helpers::Protocol::create( + uefi_command_internal::PipeProtocol::new(), + simple_text_output::PROTOCOL_GUID, + ) + .map(Some), + Stdio::Null => helpers::Protocol::create( + uefi_command_internal::PipeProtocol::null(), + simple_text_output::PROTOCOL_GUID, + ) + .map(Some), + Stdio::Inherit => Ok(None), + } + } + pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { let mut cmd = uefi_command_internal::Command::load_image(&self.prog)?; - let stdout: helpers::Protocol = + let stdout: Option> = match self.stdout.take() { - Some(s) => helpers::Protocol::create(s, simple_text_output::PROTOCOL_GUID), + Some(s) => Self::create_pipe(s), None => helpers::Protocol::create( uefi_command_internal::PipeProtocol::new(), simple_text_output::PROTOCOL_GUID, - ), + ) + .map(Some), }?; - let stderr: helpers::Protocol = + let stderr: Option> = match self.stderr.take() { - Some(s) => helpers::Protocol::create(s, simple_text_output::PROTOCOL_GUID), + Some(s) => Self::create_pipe(s), None => helpers::Protocol::create( uefi_command_internal::PipeProtocol::new(), simple_text_output::PROTOCOL_GUID, - ), + ) + .map(Some), }?; - cmd.stdout_init(stdout)?; - cmd.stderr_init(stderr)?; + if let Some(stdout) = stdout { + cmd.stdout_init(stdout)?; + } + if let Some(stderr) = stderr { + cmd.stderr_init(stderr)?; + } let stat = cmd.start_image()?; + let stdout = cmd.stdout()?; let stderr = cmd.stderr()?; From ef6b1730a134fb139808a952346137ffddf2339f Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Fri, 29 Mar 2024 19:44:37 +0530 Subject: [PATCH 22/36] uefi: process: Add support for args Also fix stdio inherit Signed-off-by: Ayush Singh --- std/src/sys/pal/uefi/process.rs | 76 ++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/std/src/sys/pal/uefi/process.rs b/std/src/sys/pal/uefi/process.rs index ed25ad81bbdc9..6b431553f8aa8 100644 --- a/std/src/sys/pal/uefi/process.rs +++ b/std/src/sys/pal/uefi/process.rs @@ -23,6 +23,7 @@ use super::helpers; pub struct Command { prog: OsString, + args: OsString, stdout: Option, stderr: Option, } @@ -45,11 +46,17 @@ pub enum Stdio { impl Command { pub fn new(program: &OsStr) -> Command { - Command { prog: program.to_os_string(), stdout: None, stderr: None } + Command { + prog: program.to_os_string(), + args: program.to_os_string(), + stdout: None, + stderr: None, + } } - pub fn arg(&mut self, _arg: &OsStr) { - panic!("unsupported") + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(" "); + self.args.push(arg); } pub fn env_mut(&mut self) -> &mut CommandEnv { @@ -137,11 +144,17 @@ impl Command { .map(Some), }?; - if let Some(stdout) = stdout { - cmd.stdout_init(stdout)?; - } - if let Some(stderr) = stderr { - cmd.stderr_init(stderr)?; + match stdout { + Some(stdout) => cmd.stdout_init(stdout), + None => cmd.stdout_inherit(), + }; + match stderr { + Some(stderr) => cmd.stderr_init(stderr), + None => cmd.stderr_inherit(), + }; + + if !self.args.is_empty() { + cmd.set_args(&self.args); } let stat = cmd.start_image()?; @@ -308,8 +321,8 @@ mod uefi_command_internal { use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_io_error}; use crate::mem::MaybeUninit; - use crate::os::uefi::env::{boot_services, image_handle}; - use crate::os::uefi::ffi::OsStringExt; + use crate::os::uefi::env::{boot_services, image_handle, system_table}; + use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::ptr::NonNull; use crate::slice; use crate::sys_common::wstr::WStrUnits; @@ -319,6 +332,7 @@ mod uefi_command_internal { stdout: Option>, stderr: Option>, st: Box, + args: Option>, } impl Command { @@ -326,7 +340,7 @@ mod uefi_command_internal { handle: NonNull, st: Box, ) -> Self { - Self { handle, stdout: None, stderr: None, st } + Self { handle, stdout: None, stderr: None, st, args: None } } pub fn load_image(p: &OsStr) -> io::Result { @@ -391,30 +405,34 @@ mod uefi_command_internal { Ok(r) } - pub fn stdout_init( - &mut self, - mut protocol: helpers::Protocol, - ) -> io::Result<()> { + pub fn stdout_init(&mut self, mut protocol: helpers::Protocol) { self.st.console_out_handle = protocol.handle().as_ptr(); self.st.con_out = protocol.as_mut() as *mut PipeProtocol as *mut simple_text_output::Protocol; self.stdout = Some(protocol); + } - Ok(()) + pub fn stdout_inherit(&mut self) { + let st: NonNull = system_table().cast(); + + self.st.console_out_handle = unsafe { (*st.as_ptr()).console_out_handle }; + self.st.con_out = unsafe { (*st.as_ptr()).con_out }; } - pub fn stderr_init( - &mut self, - mut protocol: helpers::Protocol, - ) -> io::Result<()> { + pub fn stderr_init(&mut self, mut protocol: helpers::Protocol) { self.st.standard_error_handle = protocol.handle().as_ptr(); self.st.std_err = protocol.as_mut() as *mut PipeProtocol as *mut simple_text_output::Protocol; self.stderr = Some(protocol); + } + + pub fn stderr_inherit(&mut self) { + let st: NonNull = system_table().cast(); - Ok(()) + self.st.standard_error_handle = unsafe { (*st.as_ptr()).standard_error_handle }; + self.st.std_err = unsafe { (*st.as_ptr()).std_err }; } pub fn stderr(&self) -> io::Result> { @@ -430,6 +448,22 @@ mod uefi_command_internal { None => Ok(Vec::new()), } } + + pub fn set_args(&mut self, args: &OsStr) { + let loaded_image: NonNull = + helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); + + let mut args = args.encode_wide().collect::>(); + let args_size = (crate::mem::size_of::() * args.len()) as u32; + + unsafe { + (*loaded_image.as_ptr()).load_options = + args.as_mut_ptr() as *mut crate::ffi::c_void; + (*loaded_image.as_ptr()).load_options_size = args_size; + } + + self.args = Some(args); + } } impl Drop for Command { From afe1ef08b0ec198c1e016ead3ea10d50a220d035 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Fri, 29 Mar 2024 20:31:56 +0530 Subject: [PATCH 23/36] uefi: process: Add CommandArgs support Signed-off-by: Ayush Singh --- std/src/sys/pal/uefi/process.rs | 40 +++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/std/src/sys/pal/uefi/process.rs b/std/src/sys/pal/uefi/process.rs index 6b431553f8aa8..2a32341bf6372 100644 --- a/std/src/sys/pal/uefi/process.rs +++ b/std/src/sys/pal/uefi/process.rs @@ -4,7 +4,6 @@ use crate::ffi::OsStr; use crate::ffi::OsString; use crate::fmt; use crate::io; -use crate::marker::PhantomData; use crate::num::NonZero; use crate::num::NonZeroI32; use crate::path::Path; @@ -23,7 +22,7 @@ use super::helpers; pub struct Command { prog: OsString, - args: OsString, + args: Vec, stdout: Option, stderr: Option, } @@ -48,15 +47,14 @@ impl Command { pub fn new(program: &OsStr) -> Command { Command { prog: program.to_os_string(), - args: program.to_os_string(), + args: Vec::from([program.to_os_string()]), stdout: None, stderr: None, } } pub fn arg(&mut self, arg: &OsStr) { - self.args.push(" "); - self.args.push(arg); + self.args.push(arg.to_os_string()); } pub fn env_mut(&mut self) -> &mut CommandEnv { @@ -80,11 +78,11 @@ impl Command { } pub fn get_program(&self) -> &OsStr { - panic!("unsupported") + self.prog.as_ref() } pub fn get_args(&self) -> CommandArgs<'_> { - panic!("unsupported") + CommandArgs { iter: self.args.iter() } } pub fn get_envs(&self) -> CommandEnvs<'_> { @@ -153,8 +151,15 @@ impl Command { None => cmd.stderr_inherit(), }; - if !self.args.is_empty() { - cmd.set_args(&self.args); + if self.args.len() > 1 { + let args = self.args.iter().fold(OsString::new(), |mut acc, arg| { + if !acc.is_empty() { + acc.push(" "); + } + acc.push(arg); + acc + }); + cmd.set_args(&args); } let stat = cmd.start_image()?; @@ -293,24 +298,31 @@ 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(|x| x.as_ref()) } fn size_hint(&self) -> (usize, Option) { - (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() } } From c6cb67c41a301080ed0e3d7f97634d1e88ae4404 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Fri, 29 Mar 2024 20:51:48 +0530 Subject: [PATCH 24/36] uefi: process: Final Touchups Signed-off-by: Ayush Singh --- std/src/sys/pal/uefi/process.rs | 38 ++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/std/src/sys/pal/uefi/process.rs b/std/src/sys/pal/uefi/process.rs index 2a32341bf6372..7075af186f9bd 100644 --- a/std/src/sys/pal/uefi/process.rs +++ b/std/src/sys/pal/uefi/process.rs @@ -35,8 +35,6 @@ pub struct StdioPipes { pub stderr: Option, } -// 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. pub enum Stdio { Inherit, Null, @@ -45,12 +43,7 @@ pub enum Stdio { impl Command { pub fn new(program: &OsStr) -> Command { - Command { - prog: program.to_os_string(), - args: Vec::from([program.to_os_string()]), - stdout: None, - stderr: None, - } + Command { prog: program.to_os_string(), args: Vec::new(), stdout: None, stderr: None } } pub fn arg(&mut self, arg: &OsStr) { @@ -122,6 +115,7 @@ impl Command { pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { let mut cmd = uefi_command_internal::Command::load_image(&self.prog)?; + /* Setup Stdout */ let stdout: Option> = match self.stdout.take() { Some(s) => Self::create_pipe(s), @@ -131,7 +125,12 @@ impl Command { ) .map(Some), }?; + match stdout { + Some(stdout) => cmd.stdout_init(stdout), + None => cmd.stdout_inherit(), + }; + /* Setup Stderr */ let stderr: Option> = match self.stderr.take() { Some(s) => Self::create_pipe(s), @@ -141,21 +140,15 @@ impl Command { ) .map(Some), }?; - - match stdout { - Some(stdout) => cmd.stdout_init(stdout), - None => cmd.stdout_inherit(), - }; match stderr { Some(stderr) => cmd.stderr_init(stderr), None => cmd.stderr_inherit(), }; - if self.args.len() > 1 { - let args = self.args.iter().fold(OsString::new(), |mut acc, arg| { - if !acc.is_empty() { - acc.push(" "); - } + /* No reason to set args if only program name is preset */ + if !self.args.is_empty() { + let args = self.args.iter().fold(OsString::from(&self.prog), |mut acc, arg| { + acc.push(" "); acc.push(arg); acc }); @@ -202,7 +195,11 @@ impl From for Stdio { } impl fmt::Debug for Command { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.prog.fmt(f)?; + for arg in &self.args { + arg.fmt(f)?; + } Ok(()) } } @@ -303,9 +300,11 @@ pub struct CommandArgs<'a> { impl<'a> Iterator for CommandArgs<'a> { type Item = &'a OsStr; + fn next(&mut self) -> Option<&'a OsStr> { self.iter.next().map(|x| x.as_ref()) } + fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } @@ -315,6 +314,7 @@ impl<'a> ExactSizeIterator for CommandArgs<'a> { fn len(&self) -> usize { self.iter.len() } + fn is_empty(&self) -> bool { self.iter.is_empty() } From 8d5cf50b19d57bef88f8e70e2ecef0d61df471a2 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sun, 31 Mar 2024 01:06:33 +0530 Subject: [PATCH 25/36] uefi: process: Fixes from PR - Update system table crc32 - Fix unsound use of Box - Free exit data - Code improvements - Introduce OwnedTable - Update r-efi to latest version - Use extended_varargs_abi_support for install_multiple_protocol_interfaces and uninstall_multiple_protocol_interfaces - Fix comments - Stub out args implementation Signed-off-by: Ayush Singh --- std/Cargo.toml | 2 +- std/src/lib.rs | 1 + std/src/sys/pal/uefi/helpers.rs | 131 ++++++++++++----- std/src/sys/pal/uefi/process.rs | 250 ++++++++++++++++++-------------- 4 files changed, 232 insertions(+), 152 deletions(-) diff --git a/std/Cargo.toml b/std/Cargo.toml index b991b1cf22dd8..3db2f1138605d 100644 --- a/std/Cargo.toml +++ b/std/Cargo.toml @@ -56,7 +56,7 @@ hermit-abi = { version = "0.4.0", features = ['rustc-dep-of-std'], public = true wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } [target.'cfg(target_os = "uefi")'.dependencies] -r-efi = { version = "4.2.0", features = ['rustc-dep-of-std'] } +r-efi = { version = "4.5.0", features = ['rustc-dep-of-std'] } r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std'] } [features] diff --git a/std/src/lib.rs b/std/src/lib.rs index f0a73a308a4a4..eb30a3355e5b6 100644 --- a/std/src/lib.rs +++ b/std/src/lib.rs @@ -293,6 +293,7 @@ #![feature(doc_masked)] #![feature(doc_notable_trait)] #![feature(dropck_eyepatch)] +#![feature(extended_varargs_abi_support)] #![feature(f128)] #![feature(f16)] #![feature(if_let_guard)] diff --git a/std/src/sys/pal/uefi/helpers.rs b/std/src/sys/pal/uefi/helpers.rs index c2419bbb58573..29984a915b894 100644 --- a/std/src/sys/pal/uefi/helpers.rs +++ b/std/src/sys/pal/uefi/helpers.rs @@ -21,6 +21,12 @@ use crate::slice; use crate::sync::atomic::{AtomicPtr, Ordering}; use crate::sys_common::wstr::WStrUnits; +type BootInstallMultipleProtocolInterfaces = + unsafe extern "efiapi" fn(_: *mut r_efi::efi::Handle, _: ...) -> r_efi::efi::Status; + +type BootUninstallMultipleProtocolInterfaces = + unsafe extern "efiapi" fn(_: r_efi::efi::Handle, _: ...) -> r_efi::efi::Status; + const BOOT_SERVICES_UNAVAILABLE: io::Error = const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); @@ -231,6 +237,13 @@ impl DevicePath { protocol: NonNull, ) -> io::Result { let path_vec = p.encode_wide().chain(Some(0)).collect::>(); + if path_vec[..path_vec.len() - 1].contains(&0) { + return Err(const_io_error!( + io::ErrorKind::InvalidInput, + "strings passed to UEFI cannot contain NULs", + )); + } + let path = unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) }; @@ -267,17 +280,9 @@ impl DevicePath { "DevicePathFromText Protocol not found" )) } -} - -impl AsRef for DevicePath { - fn as_ref(&self) -> &r_efi::protocols::device_path::Protocol { - unsafe { self.0.as_ref() } - } -} -impl AsMut for DevicePath { - fn as_mut(&mut self) -> &mut r_efi::protocols::device_path::Protocol { - unsafe { self.0.as_mut() } + pub(crate) fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { + self.0.as_ptr() } } @@ -292,44 +297,42 @@ impl Drop for DevicePath { } } -pub(crate) struct Protocol { +pub(crate) struct OwnedProtocol { guid: r_efi::efi::Guid, handle: NonNull, - protocol: Box, + protocol: *mut T, } -impl Protocol { - const fn new( - guid: r_efi::efi::Guid, - handle: NonNull, - protocol: Box, - ) -> Self { - Self { guid, handle, protocol } - } - - pub(crate) fn create(protocol: T, mut guid: r_efi::efi::Guid) -> io::Result { - let boot_services: NonNull = +impl OwnedProtocol { + // FIXME: Consider using unsafe trait for matching protocol with guid + pub(crate) unsafe fn create(protocol: T, mut guid: r_efi::efi::Guid) -> io::Result { + let bt: NonNull = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); - let mut protocol = Box::new(protocol); + let protocol: *mut T = Box::into_raw(Box::new(protocol)); let mut handle: r_efi::efi::Handle = crate::ptr::null_mut(); + // FIXME: Move into r-efi once extended_varargs_abi_support is stablized + let func: BootInstallMultipleProtocolInterfaces = + unsafe { crate::mem::transmute((*bt.as_ptr()).install_multiple_protocol_interfaces) }; + let r = unsafe { - ((*boot_services.as_ptr()).install_protocol_interface)( + func( &mut handle, - &mut guid, - r_efi::efi::NATIVE_INTERFACE, - protocol.as_mut() as *mut T as *mut crate::ffi::c_void, + &mut guid as *mut _ as *mut crate::ffi::c_void, + protocol as *mut crate::ffi::c_void, + crate::ptr::null_mut() as *mut crate::ffi::c_void, ) }; if r.is_error() { + drop(unsafe { Box::from_raw(protocol) }); return Err(crate::io::Error::from_raw_os_error(r.as_usize())); }; let handle = NonNull::new(handle) .ok_or(io::const_io_error!(io::ErrorKind::Uncategorized, "found null handle"))?; - Ok(Self::new(guid, handle, protocol)) + Ok(Self { guid, handle, protocol }) } pub(crate) fn handle(&self) -> NonNull { @@ -337,29 +340,79 @@ impl Protocol { } } -impl Drop for Protocol { +impl Drop for OwnedProtocol { fn drop(&mut self) { + // Do not deallocate a runtime protocol if let Some(bt) = boot_services() { let bt: NonNull = bt.cast(); - unsafe { - ((*bt.as_ptr()).uninstall_protocol_interface)( + // FIXME: Move into r-efi once extended_varargs_abi_support is stablized + let func: BootUninstallMultipleProtocolInterfaces = unsafe { + crate::mem::transmute((*bt.as_ptr()).uninstall_multiple_protocol_interfaces) + }; + let status = unsafe { + func( self.handle.as_ptr(), - &mut self.guid, - self.protocol.as_mut() as *mut T as *mut crate::ffi::c_void, + &mut self.guid as *mut _ as *mut crate::ffi::c_void, + self.protocol as *mut crate::ffi::c_void, + crate::ptr::null_mut() as *mut crate::ffi::c_void, ) }; + + // Leak the protocol in case uninstall fails + if status == r_efi::efi::Status::SUCCESS { + let _ = unsafe { Box::from_raw(self.protocol) }; + } } } } -impl AsRef for Protocol { +impl AsRef for OwnedProtocol { fn as_ref(&self) -> &T { - &self.protocol + unsafe { self.protocol.as_ref().unwrap() } + } +} + +pub(crate) struct OwnedTable { + layout: crate::alloc::Layout, + ptr: *mut T, +} + +impl OwnedTable { + pub(crate) fn from_table_header(hdr: &r_efi::efi::TableHeader) -> Self { + let header_size = hdr.header_size as usize; + let layout = crate::alloc::Layout::from_size_align(header_size, 8).unwrap(); + let ptr = unsafe { crate::alloc::alloc(layout) as *mut T }; + Self { layout, ptr } + } + + pub(crate) const fn as_ptr(&self) -> *const T { + self.ptr + } + + pub(crate) const fn as_mut_ptr(&self) -> *mut T { + self.ptr } } -impl AsMut for Protocol { - fn as_mut(&mut self) -> &mut T { - &mut self.protocol +impl OwnedTable { + pub(crate) fn from_table(tbl: *const r_efi::efi::SystemTable) -> Self { + let hdr = unsafe { (*tbl).hdr }; + + let owned_tbl = Self::from_table_header(&hdr); + unsafe { + crate::ptr::copy_nonoverlapping( + tbl as *const u8, + owned_tbl.as_mut_ptr() as *mut u8, + hdr.header_size as usize, + ) + }; + + owned_tbl + } +} + +impl Drop for OwnedTable { + fn drop(&mut self) { + unsafe { crate::alloc::dealloc(self.ptr as *mut u8, self.layout) }; } } diff --git a/std/src/sys/pal/uefi/process.rs b/std/src/sys/pal/uefi/process.rs index 7075af186f9bd..5c7c8415ee295 100644 --- a/std/src/sys/pal/uefi/process.rs +++ b/std/src/sys/pal/uefi/process.rs @@ -20,9 +20,9 @@ use super::helpers; // Command //////////////////////////////////////////////////////////////////////////////// +#[derive(Debug)] pub struct Command { prog: OsString, - args: Vec, stdout: Option, stderr: Option, } @@ -35,6 +35,7 @@ pub struct StdioPipes { pub stderr: Option, } +#[derive(Copy, Clone, Debug)] pub enum Stdio { Inherit, Null, @@ -43,11 +44,12 @@ pub enum Stdio { impl Command { pub fn new(program: &OsStr) -> Command { - Command { prog: program.to_os_string(), args: Vec::new(), stdout: None, stderr: None } + Command { prog: program.to_os_string(), stdout: None, stderr: None } } - pub fn arg(&mut self, arg: &OsStr) { - self.args.push(arg.to_os_string()); + // FIXME: Implement arguments as reverse of parsing algorithm + pub fn arg(&mut self, _arg: &OsStr) { + panic!("unsupported") } pub fn env_mut(&mut self) -> &mut CommandEnv { @@ -75,7 +77,7 @@ impl Command { } pub fn get_args(&self) -> CommandArgs<'_> { - CommandArgs { iter: self.args.iter() } + panic!("unsupported") } pub fn get_envs(&self) -> CommandEnvs<'_> { @@ -96,65 +98,47 @@ impl Command { fn create_pipe( s: Stdio, - ) -> io::Result>> { + ) -> io::Result>> { match s { - Stdio::MakePipe => helpers::Protocol::create( - uefi_command_internal::PipeProtocol::new(), - simple_text_output::PROTOCOL_GUID, - ) + Stdio::MakePipe => unsafe { + helpers::OwnedProtocol::create( + uefi_command_internal::PipeProtocol::new(), + simple_text_output::PROTOCOL_GUID, + ) + } .map(Some), - Stdio::Null => helpers::Protocol::create( - uefi_command_internal::PipeProtocol::null(), - simple_text_output::PROTOCOL_GUID, - ) + Stdio::Null => unsafe { + helpers::OwnedProtocol::create( + uefi_command_internal::PipeProtocol::null(), + simple_text_output::PROTOCOL_GUID, + ) + } .map(Some), Stdio::Inherit => Ok(None), } } pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - let mut cmd = uefi_command_internal::Command::load_image(&self.prog)?; - - /* Setup Stdout */ - let stdout: Option> = - match self.stdout.take() { - Some(s) => Self::create_pipe(s), - None => helpers::Protocol::create( - uefi_command_internal::PipeProtocol::new(), - simple_text_output::PROTOCOL_GUID, - ) - .map(Some), - }?; - match stdout { - Some(stdout) => cmd.stdout_init(stdout), - None => cmd.stdout_inherit(), + let mut cmd = uefi_command_internal::Image::load_image(&self.prog)?; + + // Setup Stdout + let stdout = self.stdout.unwrap_or(Stdio::MakePipe); + let stdout = Self::create_pipe(stdout)?; + if let Some(con) = stdout { + cmd.stdout_init(con) + } else { + cmd.stdout_inherit() }; - /* Setup Stderr */ - let stderr: Option> = - match self.stderr.take() { - Some(s) => Self::create_pipe(s), - None => helpers::Protocol::create( - uefi_command_internal::PipeProtocol::new(), - simple_text_output::PROTOCOL_GUID, - ) - .map(Some), - }?; - match stderr { - Some(stderr) => cmd.stderr_init(stderr), - None => cmd.stderr_inherit(), + // Setup Stderr + let stderr = self.stderr.unwrap_or(Stdio::MakePipe); + let stderr = Self::create_pipe(stderr)?; + if let Some(con) = stderr { + cmd.stderr_init(con) + } else { + cmd.stderr_inherit() }; - /* No reason to set args if only program name is preset */ - if !self.args.is_empty() { - let args = self.args.iter().fold(OsString::from(&self.prog), |mut acc, arg| { - acc.push(" "); - acc.push(arg); - acc - }); - cmd.set_args(&args); - } - let stat = cmd.start_image()?; let stdout = cmd.stdout()?; @@ -194,16 +178,6 @@ impl From for Stdio { } } -impl fmt::Debug for Command { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.prog.fmt(f)?; - for arg in &self.args { - arg.fmt(f)?; - } - Ok(()) - } -} - #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[non_exhaustive] pub struct ExitStatus(r_efi::efi::Status); @@ -326,6 +300,7 @@ impl<'a> fmt::Debug for CommandArgs<'a> { } } +#[allow(dead_code)] mod uefi_command_internal { use r_efi::protocols::{loaded_image, simple_text_output}; @@ -337,26 +312,20 @@ mod uefi_command_internal { use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::ptr::NonNull; use crate::slice; + use crate::sys::pal::uefi::helpers::OwnedTable; use crate::sys_common::wstr::WStrUnits; - pub struct Command { + pub struct Image { handle: NonNull, - stdout: Option>, - stderr: Option>, - st: Box, + stdout: Option>, + stderr: Option>, + st: OwnedTable, args: Option>, } - impl Command { - const fn new( - handle: NonNull, - st: Box, - ) -> Self { - Self { handle, stdout: None, stderr: None, st, args: None } - } - + impl Image { pub fn load_image(p: &OsStr) -> io::Result { - let mut path = helpers::DevicePath::from_text(p)?; + let path = helpers::DevicePath::from_text(p)?; let boot_services: NonNull = boot_services() .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))? .cast(); @@ -367,7 +336,7 @@ mod uefi_command_internal { ((*boot_services.as_ptr()).load_image)( r_efi::efi::Boolean::FALSE, image_handle.as_ptr(), - path.as_mut(), + path.as_ptr(), crate::ptr::null_mut(), 0, child_handle.as_mut_ptr(), @@ -382,69 +351,93 @@ mod uefi_command_internal { let loaded_image: NonNull = helpers::open_protocol(child_handle, loaded_image::PROTOCOL_GUID).unwrap(); - let mut st: Box = - Box::new(unsafe { crate::ptr::read((*loaded_image.as_ptr()).system_table) }); + let st = OwnedTable::from_table(unsafe { (*loaded_image.as_ptr()).system_table }); - unsafe { - (*loaded_image.as_ptr()).system_table = st.as_mut(); - } - - Ok(Self::new(child_handle, st)) + Ok(Self { handle: child_handle, stdout: None, stderr: None, st, args: None }) } } - pub fn start_image(&self) -> io::Result { + pub fn start_image(&mut self) -> io::Result { + self.update_st_crc32()?; + + // Use our system table instead of the default one + let loaded_image: NonNull = + helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); + unsafe { + (*loaded_image.as_ptr()).system_table = self.st.as_mut_ptr(); + } + let boot_services: NonNull = boot_services() .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))? .cast(); - let mut exit_data_size: MaybeUninit = MaybeUninit::uninit(); + let mut exit_data_size: usize = 0; let mut exit_data: MaybeUninit<*mut u16> = MaybeUninit::uninit(); let r = unsafe { ((*boot_services.as_ptr()).start_image)( self.handle.as_ptr(), - exit_data_size.as_mut_ptr(), + &mut exit_data_size, exit_data.as_mut_ptr(), ) }; // Drop exitdata - unsafe { - exit_data_size.assume_init_drop(); - exit_data.assume_init_drop(); + if exit_data_size != 0 { + unsafe { + let exit_data = exit_data.assume_init(); + ((*boot_services.as_ptr()).free_pool)(exit_data as *mut crate::ffi::c_void); + } } Ok(r) } - pub fn stdout_init(&mut self, mut protocol: helpers::Protocol) { - self.st.console_out_handle = protocol.handle().as_ptr(); - self.st.con_out = - protocol.as_mut() as *mut PipeProtocol as *mut simple_text_output::Protocol; + fn set_stdout( + &mut self, + handle: r_efi::efi::Handle, + protocol: *mut simple_text_output::Protocol, + ) { + unsafe { + (*self.st.as_mut_ptr()).console_out_handle = handle; + (*self.st.as_mut_ptr()).con_out = protocol; + } + } + + fn set_stderr( + &mut self, + handle: r_efi::efi::Handle, + protocol: *mut simple_text_output::Protocol, + ) { + unsafe { + (*self.st.as_mut_ptr()).standard_error_handle = handle; + (*self.st.as_mut_ptr()).std_err = protocol; + } + } + pub fn stdout_init(&mut self, protocol: helpers::OwnedProtocol) { + self.set_stdout( + protocol.handle().as_ptr(), + protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol, + ); self.stdout = Some(protocol); } pub fn stdout_inherit(&mut self) { let st: NonNull = system_table().cast(); - - self.st.console_out_handle = unsafe { (*st.as_ptr()).console_out_handle }; - self.st.con_out = unsafe { (*st.as_ptr()).con_out }; + unsafe { self.set_stdout((*st.as_ptr()).console_out_handle, (*st.as_ptr()).con_out) } } - pub fn stderr_init(&mut self, mut protocol: helpers::Protocol) { - self.st.standard_error_handle = protocol.handle().as_ptr(); - self.st.std_err = - protocol.as_mut() as *mut PipeProtocol as *mut simple_text_output::Protocol; - + pub fn stderr_init(&mut self, protocol: helpers::OwnedProtocol) { + self.set_stderr( + protocol.handle().as_ptr(), + protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol, + ); self.stderr = Some(protocol); } pub fn stderr_inherit(&mut self) { let st: NonNull = system_table().cast(); - - self.st.standard_error_handle = unsafe { (*st.as_ptr()).standard_error_handle }; - self.st.std_err = unsafe { (*st.as_ptr()).std_err }; + unsafe { self.set_stderr((*st.as_ptr()).standard_error_handle, (*st.as_ptr()).std_err) } } pub fn stderr(&self) -> io::Result> { @@ -476,9 +469,37 @@ mod uefi_command_internal { self.args = Some(args); } + + fn update_st_crc32(&mut self) -> io::Result<()> { + let bt: NonNull = boot_services().unwrap().cast(); + let st_size = unsafe { (*self.st.as_ptr()).hdr.header_size as usize }; + let mut crc32: u32 = 0; + + // Set crc to 0 before calcuation + unsafe { + (*self.st.as_mut_ptr()).hdr.crc32 = 0; + } + + let r = unsafe { + ((*bt.as_ptr()).calculate_crc32)( + self.st.as_mut_ptr() as *mut crate::ffi::c_void, + st_size, + &mut crc32, + ) + }; + + if r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + unsafe { + (*self.st.as_mut_ptr()).hdr.crc32 = crc32; + } + Ok(()) + } + } } - impl Drop for Command { + impl Drop for Image { fn drop(&mut self) { if let Some(bt) = boot_services() { let bt: NonNull = bt.cast(); @@ -501,13 +522,12 @@ mod uefi_command_internal { set_cursor_position: simple_text_output::ProtocolSetCursorPosition, enable_cursor: simple_text_output::ProtocolEnableCursor, mode: *mut simple_text_output::Mode, - _mode: Box, _buffer: Vec, } impl PipeProtocol { pub fn new() -> Self { - let mut mode = Box::new(simple_text_output::Mode { + let mode = Box::new(simple_text_output::Mode { max_mode: 0, mode: 0, attribute: 0, @@ -525,14 +545,13 @@ mod uefi_command_internal { clear_screen: Self::clear_screen, set_cursor_position: Self::set_cursor_position, enable_cursor: Self::enable_cursor, - mode: mode.as_mut(), - _mode: mode, + mode: Box::into_raw(mode), _buffer: Vec::new(), } } pub fn null() -> Self { - let mut mode = Box::new(simple_text_output::Mode { + let mode = Box::new(simple_text_output::Mode { max_mode: 0, mode: 0, attribute: 0, @@ -550,8 +569,7 @@ mod uefi_command_internal { clear_screen: Self::clear_screen, set_cursor_position: Self::set_cursor_position, enable_cursor: Self::enable_cursor, - mode: mode.as_mut(), - _mode: mode, + mode: Box::into_raw(mode), _buffer: Vec::new(), } } @@ -660,4 +678,12 @@ mod uefi_command_internal { r_efi::efi::Status::UNSUPPORTED } } + + impl Drop for PipeProtocol { + fn drop(&mut self) { + unsafe { + let _ = Box::from_raw(self.mode); + } + } + } } From dcb98546d312fdecf4de78a23a47367ff65cb392 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 17 Jul 2024 12:57:44 -0700 Subject: [PATCH 26/36] kmc-solid: forbid(unsafe_op_in_unsafe_fn) --- std/src/os/solid/io.rs | 1 - std/src/os/solid/mod.rs | 1 + std/src/sys/pal/solid/mod.rs | 2 +- std/src/sys/sync/mutex/itron.rs | 1 + std/src/sys/sync/rwlock/solid.rs | 1 + 5 files changed, 4 insertions(+), 2 deletions(-) diff --git a/std/src/os/solid/io.rs b/std/src/os/solid/io.rs index 19b4fe22093c3..e75bcf74e5cd3 100644 --- a/std/src/os/solid/io.rs +++ b/std/src/os/solid/io.rs @@ -44,7 +44,6 @@ //! //! [`BorrowedFd<'a>`]: crate::os::solid::io::BorrowedFd -#![deny(unsafe_op_in_unsafe_fn)] #![unstable(feature = "solid_ext", issue = "none")] use crate::fmt; diff --git a/std/src/os/solid/mod.rs b/std/src/os/solid/mod.rs index 0bb83c73ddf7c..75824048e2498 100644 --- a/std/src/os/solid/mod.rs +++ b/std/src/os/solid/mod.rs @@ -1,4 +1,5 @@ #![stable(feature = "rust1", since = "1.0.0")] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod ffi; pub mod io; diff --git a/std/src/sys/pal/solid/mod.rs b/std/src/sys/pal/solid/mod.rs index 9a7741ddda71e..0b158fb63df70 100644 --- a/std/src/sys/pal/solid/mod.rs +++ b/std/src/sys/pal/solid/mod.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] #![allow(missing_docs, nonstandard_style)] -#![deny(unsafe_op_in_unsafe_fn)] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod abi; diff --git a/std/src/sys/sync/mutex/itron.rs b/std/src/sys/sync/mutex/itron.rs index 4ba32a8fbcd69..b29c7e1d03444 100644 --- a/std/src/sys/sync/mutex/itron.rs +++ b/std/src/sys/sync/mutex/itron.rs @@ -1,5 +1,6 @@ //! Mutex implementation backed by μITRON mutexes. Assumes `acre_mtx` and //! `TA_INHERIT` are available. +#![forbid(unsafe_op_in_unsafe_fn)] use crate::sys::pal::itron::{ abi, diff --git a/std/src/sys/sync/rwlock/solid.rs b/std/src/sys/sync/rwlock/solid.rs index 7558eee8edd33..a8fef685ceb27 100644 --- a/std/src/sys/sync/rwlock/solid.rs +++ b/std/src/sys/sync/rwlock/solid.rs @@ -1,4 +1,5 @@ //! A readers-writer lock implementation backed by the SOLID kernel extension. +#![forbid(unsafe_op_in_unsafe_fn)] use crate::sys::pal::{ abi, From eb09be43d35195c58fc1ef90a533183f02e28fe3 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 17 Jul 2024 13:10:12 -0700 Subject: [PATCH 27/36] std: forbid unwrapped unsafe in unsupported_backslash --- std/src/sys/path/unsupported_backslash.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/std/src/sys/path/unsupported_backslash.rs b/std/src/sys/path/unsupported_backslash.rs index 7045c9be25b08..855f443678c6c 100644 --- a/std/src/sys/path/unsupported_backslash.rs +++ b/std/src/sys/path/unsupported_backslash.rs @@ -1,3 +1,4 @@ +#![forbid(unsafe_op_in_unsafe_fn)] use crate::ffi::OsStr; use crate::io; use crate::path::{Path, PathBuf, Prefix}; From 3b2536ec2064f7eab8b1bdd9556cbf6e4618e086 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sat, 20 Jul 2024 12:37:36 +0000 Subject: [PATCH 28/36] Remove _tls_used hack --- std/src/sys/thread_local/guard/windows.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/std/src/sys/thread_local/guard/windows.rs b/std/src/sys/thread_local/guard/windows.rs index f6cd457046ffc..e08ac44e1af88 100644 --- a/std/src/sys/thread_local/guard/windows.rs +++ b/std/src/sys/thread_local/guard/windows.rs @@ -78,19 +78,6 @@ pub fn enable() { pub static CALLBACK: unsafe extern "system" fn(*mut c_void, u32, *mut c_void) = tls_callback; unsafe extern "system" fn tls_callback(_h: *mut c_void, dw_reason: u32, _pv: *mut c_void) { - // See comments above for what this is doing. Note that we don't need this - // trickery on GNU windows, just on MSVC. - #[cfg(all(target_env = "msvc", not(target_thread_local)))] - { - extern "C" { - static _tls_used: u8; - } - - unsafe { - ptr::from_ref(&_tls_used).read_volatile(); - } - } - if dw_reason == c::DLL_THREAD_DETACH || dw_reason == c::DLL_PROCESS_DETACH { #[cfg(target_thread_local)] unsafe { From 321dbf82a7809f40ac417ce64ecd3facbfa9df64 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 10 Jul 2024 19:42:54 +0200 Subject: [PATCH 29/36] Deal with invalid UTF-8 from `gai_strerror` When the system is using a non-UTF-8 locale, the value will indeed not be UTF-8. That sucks for everyone involved, but is no reason for panic. We can "handle" this gracefully by just using from lossy, replacing the invalid UTF-8 with the ? and keeping the accidentally valid UTF-8. Good luck when debugging, but at least it's not a crash. We already do this for `strerror_r`. --- std/src/sys/pal/unix/net.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/std/src/sys/pal/unix/net.rs b/std/src/sys/pal/unix/net.rs index b8dc1538a6378..bedb06043a7b4 100644 --- a/std/src/sys/pal/unix/net.rs +++ b/std/src/sys/pal/unix/net.rs @@ -4,7 +4,6 @@ use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{Shutdown, SocketAddr}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::str; use crate::sys::fd::FileDesc; use crate::sys::pal::unix::IsMinusOne; use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; @@ -47,7 +46,9 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> { #[cfg(not(target_os = "espidf"))] let detail = unsafe { - str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap().to_owned() + // We can't always expect a UTF-8 environment. When we don't get that luxury, + // it's better to give a low-quality error message than none at all. + CStr::from_ptr(libc::gai_strerror(err)).to_string_lossy() }; #[cfg(target_os = "espidf")] From 0727e53d39310020735cd462d27a60b6b6e9d18a Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Fri, 19 Jul 2024 20:08:40 -0400 Subject: [PATCH 30/36] Fix warnings when checking armv6k-nintendo-3ds Also fix one instance of unsafe_op_in_unsafe_fn that's specific to horizon + vita - most others should be common with other code. --- std/src/os/horizon/mod.rs | 1 + std/src/os/horizon/raw.rs | 1 + std/src/sys/pal/unix/alloc.rs | 2 +- std/src/sys/pal/unix/process/process_unsupported.rs | 1 - 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/std/src/os/horizon/mod.rs b/std/src/os/horizon/mod.rs index 326d0ae9cb96d..14ce409f42c0b 100644 --- a/std/src/os/horizon/mod.rs +++ b/std/src/os/horizon/mod.rs @@ -1,5 +1,6 @@ //! Definitions for Horizon OS +#![forbid(unsafe_op_in_unsafe_fn)] #![stable(feature = "raw_ext", since = "1.1.0")] pub mod fs; diff --git a/std/src/os/horizon/raw.rs b/std/src/os/horizon/raw.rs index 929fa7db1f964..e5368ea265a13 100644 --- a/std/src/os/horizon/raw.rs +++ b/std/src/os/horizon/raw.rs @@ -38,6 +38,7 @@ pub type time_t = libc::time_t; #[repr(C)] #[derive(Clone)] #[stable(feature = "raw_ext", since = "1.1.0")] +#[allow(dead_code)] // This exists for parity with other `raw` modules, but isn't actually used. pub struct stat { #[stable(feature = "raw_ext", since = "1.1.0")] pub st_dev: dev_t, diff --git a/std/src/sys/pal/unix/alloc.rs b/std/src/sys/pal/unix/alloc.rs index eb3a57c212b4a..625ba5247f111 100644 --- a/std/src/sys/pal/unix/alloc.rs +++ b/std/src/sys/pal/unix/alloc.rs @@ -67,7 +67,7 @@ cfg_if::cfg_if! { ))] { #[inline] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { - libc::memalign(layout.align(), layout.size()) as *mut u8 + unsafe { libc::memalign(layout.align(), layout.size()) as *mut u8 } } } else { #[inline] diff --git a/std/src/sys/pal/unix/process/process_unsupported.rs b/std/src/sys/pal/unix/process/process_unsupported.rs index 33d359d3f8411..90d53464c83f1 100644 --- a/std/src/sys/pal/unix/process/process_unsupported.rs +++ b/std/src/sys/pal/unix/process/process_unsupported.rs @@ -1,4 +1,3 @@ -use crate::fmt; use crate::io; use crate::num::NonZero; use crate::sys::pal::unix::unsupported::*; From d0bc9a0ec5d948da9eabe1996660c1052f18aa3a Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Sat, 20 Jul 2024 16:45:33 +0200 Subject: [PATCH 31/36] Start using `#[diagnostic::do_not_recommend]` in the standard library This commit starts using `#[diagnostic::do_not_recommend]` in the standard library to improve some error messages. In this case we just hide a certain nightly only impl as suggested in #121521 --- core/src/lib.rs | 1 + core/src/option.rs | 1 + core/src/result.rs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index 49f89e702558f..02cb02dce1d87 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -165,6 +165,7 @@ #![feature(const_unsafecell_get_mut)] #![feature(const_waker)] #![feature(coverage_attribute)] +#![feature(do_not_recommend)] #![feature(duration_consts_float)] #![feature(internal_impls_macro)] #![feature(ip)] diff --git a/core/src/option.rs b/core/src/option.rs index 1a8fe1e6051ad..d93cb8d10e607 100644 --- a/core/src/option.rs +++ b/core/src/option.rs @@ -2507,6 +2507,7 @@ impl ops::FromResidual for Option { } } +#[diagnostic::do_not_recommend] #[unstable(feature = "try_trait_v2_yeet", issue = "96374")] impl ops::FromResidual> for Option { #[inline] diff --git a/core/src/result.rs b/core/src/result.rs index f8cdcc000c50e..7f278296b7b88 100644 --- a/core/src/result.rs +++ b/core/src/result.rs @@ -1990,7 +1990,7 @@ impl> ops::FromResidual> for Res } } } - +#[diagnostic::do_not_recommend] #[unstable(feature = "try_trait_v2_yeet", issue = "96374")] impl> ops::FromResidual> for Result { #[inline] From d6a36f520f5903ec5abbedf81f769593a7020b74 Mon Sep 17 00:00:00 2001 From: Aljoscha Meyer Date: Mon, 22 Jul 2024 08:17:46 +0200 Subject: [PATCH 32/36] Use given allocator instad of Global --- alloc/src/boxed.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alloc/src/boxed.rs b/alloc/src/boxed.rs index 322c0756abdb3..5117f883af2b6 100644 --- a/alloc/src/boxed.rs +++ b/alloc/src/boxed.rs @@ -867,7 +867,7 @@ impl Box<[T], A> { Ok(l) => l, Err(_) => return Err(AllocError), }; - Global.allocate(layout)?.cast() + alloc.allocate(layout)?.cast() }; unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, alloc).into_box(len)) } } @@ -906,7 +906,7 @@ impl Box<[T], A> { Ok(l) => l, Err(_) => return Err(AllocError), }; - Global.allocate_zeroed(layout)?.cast() + alloc.allocate_zeroed(layout)?.cast() }; unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, alloc).into_box(len)) } } From c039ee8ff7e17a12e2a8e5ea15fd19ce9951b506 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Fri, 31 May 2024 18:28:03 -0700 Subject: [PATCH 33/36] library: vary unsafety in bootstrapping for SEH --- panic_unwind/src/seh.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/panic_unwind/src/seh.rs b/panic_unwind/src/seh.rs index 04c3d3bf9c359..82c248c5a7ba1 100644 --- a/panic_unwind/src/seh.rs +++ b/panic_unwind/src/seh.rs @@ -157,7 +157,10 @@ mod imp { // going to be cross-lang LTOed anyway. However, using expose is shorter and // requires less unsafe. let addr: usize = ptr.expose_provenance(); + #[cfg(bootstrap)] let image_base = unsafe { addr_of!(__ImageBase) }.addr(); + #[cfg(not(bootstrap))] + let image_base = addr_of!(__ImageBase).addr(); let offset: usize = addr - image_base; Self(offset as u32) } @@ -250,7 +253,10 @@ extern "C" { // This is fine since the MSVC runtime uses string comparison on the type name // to match TypeDescriptors rather than pointer equality. static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor { + #[cfg(bootstrap)] pVFTable: unsafe { addr_of!(TYPE_INFO_VTABLE) } as *const _, + #[cfg(not(bootstrap))] + pVFTable: addr_of!(TYPE_INFO_VTABLE) as *const _, spare: core::ptr::null_mut(), name: TYPE_NAME, }; From 155aef9d64b3dc5ad38de70289f576e311736672 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 23 Jul 2024 01:12:29 -0700 Subject: [PATCH 34/36] std: Unsafe-wrap alloc code held in-common --- std/src/sys/pal/common/alloc.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/std/src/sys/pal/common/alloc.rs b/std/src/sys/pal/common/alloc.rs index 598b6db71f5de..54506c3229675 100644 --- a/std/src/sys/pal/common/alloc.rs +++ b/std/src/sys/pal/common/alloc.rs @@ -1,3 +1,4 @@ +#![forbid(unsafe_op_in_unsafe_fn)] use crate::alloc::{GlobalAlloc, Layout, System}; use crate::cmp; use crate::ptr; @@ -46,14 +47,16 @@ pub unsafe fn realloc_fallback( old_layout: Layout, new_size: usize, ) -> *mut u8 { - // Docs for GlobalAlloc::realloc require this to be valid: - let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); + // SAFETY: Docs for GlobalAlloc::realloc require this to be valid + unsafe { + let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); - let new_ptr = GlobalAlloc::alloc(alloc, new_layout); - if !new_ptr.is_null() { - let size = cmp::min(old_layout.size(), new_size); - ptr::copy_nonoverlapping(ptr, new_ptr, size); - GlobalAlloc::dealloc(alloc, ptr, old_layout); + let new_ptr = GlobalAlloc::alloc(alloc, new_layout); + if !new_ptr.is_null() { + let size = cmp::min(old_layout.size(), new_size); + ptr::copy_nonoverlapping(ptr, new_ptr, size); + GlobalAlloc::dealloc(alloc, ptr, old_layout); + } + new_ptr } - new_ptr } From de2a0378391d45f77d7aa2ee967c8a5ed4ad51b9 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 23 Jul 2024 01:16:46 -0700 Subject: [PATCH 35/36] std: Unsafe-wrap backtrace code held in-common --- std/src/sys/backtrace.rs | 118 ++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 57 deletions(-) diff --git a/std/src/sys/backtrace.rs b/std/src/sys/backtrace.rs index 7401d8ce32087..133ea520e30c7 100644 --- a/std/src/sys/backtrace.rs +++ b/std/src/sys/backtrace.rs @@ -1,4 +1,5 @@ //! Common code for printing backtraces. +#![forbid(unsafe_op_in_unsafe_fn)] use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt}; use crate::borrow::Cow; @@ -62,73 +63,76 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: // Start immediately if we're not using a short backtrace. let mut start = print_fmt != PrintFmt::Short; set_image_base(); - backtrace_rs::trace_unsynchronized(|frame| { - if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES { - return false; - } + // SAFETY: we roll our own locking in this town + unsafe { + backtrace_rs::trace_unsynchronized(|frame| { + if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES { + return false; + } - let mut hit = false; - backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { - hit = true; - - // Any frames between `__rust_begin_short_backtrace` and `__rust_end_short_backtrace` - // are omitted from the backtrace in short mode, `__rust_end_short_backtrace` will be - // called before the panic hook, so we won't ignore any frames if there is no - // invoke of `__rust_begin_short_backtrace`. - if print_fmt == PrintFmt::Short { - if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { - if start && sym.contains("__rust_begin_short_backtrace") { - start = false; - return; - } - if sym.contains("__rust_end_short_backtrace") { - start = true; - return; - } - if !start { - omitted_count += 1; + let mut hit = false; + backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { + hit = true; + + // Any frames between `__rust_begin_short_backtrace` and `__rust_end_short_backtrace` + // are omitted from the backtrace in short mode, `__rust_end_short_backtrace` will be + // called before the panic hook, so we won't ignore any frames if there is no + // invoke of `__rust_begin_short_backtrace`. + if print_fmt == PrintFmt::Short { + if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { + if start && sym.contains("__rust_begin_short_backtrace") { + start = false; + return; + } + if sym.contains("__rust_end_short_backtrace") { + start = true; + return; + } + if !start { + omitted_count += 1; + } } } - } - if start { - if omitted_count > 0 { - debug_assert!(print_fmt == PrintFmt::Short); - // only print the message between the middle of frames - if !first_omit { - let _ = writeln!( - bt_fmt.formatter(), - " [... omitted {} frame{} ...]", - omitted_count, - if omitted_count > 1 { "s" } else { "" } - ); + if start { + if omitted_count > 0 { + debug_assert!(print_fmt == PrintFmt::Short); + // only print the message between the middle of frames + if !first_omit { + let _ = writeln!( + bt_fmt.formatter(), + " [... omitted {} frame{} ...]", + omitted_count, + if omitted_count > 1 { "s" } else { "" } + ); + } + first_omit = false; + omitted_count = 0; } - first_omit = false; - omitted_count = 0; + res = bt_fmt.frame().symbol(frame, symbol); } - res = bt_fmt.frame().symbol(frame, symbol); + }); + #[cfg(target_os = "nto")] + if libc::__my_thread_exit as *mut libc::c_void == frame.ip() { + if !hit && start { + use crate::backtrace_rs::SymbolName; + res = bt_fmt.frame().print_raw( + frame.ip(), + Some(SymbolName::new("__my_thread_exit".as_bytes())), + None, + None, + ); + } + return false; } - }); - #[cfg(target_os = "nto")] - if libc::__my_thread_exit as *mut libc::c_void == frame.ip() { if !hit && start { - use crate::backtrace_rs::SymbolName; - res = bt_fmt.frame().print_raw( - frame.ip(), - Some(SymbolName::new("__my_thread_exit".as_bytes())), - None, - None, - ); + res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); } - return false; - } - if !hit && start { - res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); - } - idx += 1; - res.is_ok() - }); + idx += 1; + res.is_ok() + }) + }; res?; bt_fmt.finish()?; if print_fmt == PrintFmt::Short { From 4c4a93a480e332c9e223fea9458320b5392934e0 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Sun, 30 Jun 2024 18:23:07 +1000 Subject: [PATCH 36/36] Initial implementation of anonymous_pipe Co-authored-by: Alphyr <47725341+a1phyr@users.noreply.github.com> Co-authored-by: Jubilee <46493976+workingjubilee@users.noreply.github.com> Signed-off-by: Jiahao XU --- std/Cargo.toml | 56 ++++++++-- std/src/lib.rs | 2 + std/src/pipe.rs | 130 ++++++++++++++++++++++ std/src/sys/anonymous_pipe/mod.rs | 18 +++ std/src/sys/anonymous_pipe/tests.rs | 20 ++++ std/src/sys/anonymous_pipe/unix.rs | 103 +++++++++++++++++ std/src/sys/anonymous_pipe/unsupported.rs | 26 +++++ std/src/sys/anonymous_pipe/windows.rs | 109 ++++++++++++++++++ std/src/sys/mod.rs | 2 + std/src/sys/pal/unix/fd.rs | 5 + std/src/sys/pal/unix/pipe.rs | 9 ++ std/src/sys/pal/unsupported/pipe.rs | 15 ++- std/src/sys/pal/windows/c/bindings.txt | 1 + std/src/sys/pal/windows/c/windows_sys.rs | 1 + std/src/sys/pal/windows/handle.rs | 7 ++ std/src/sys/pal/windows/pipe.rs | 24 +++- std/tests/pipe_subprocess.rs | 39 +++++++ 17 files changed, 551 insertions(+), 16 deletions(-) create mode 100644 std/src/pipe.rs create mode 100644 std/src/sys/anonymous_pipe/mod.rs create mode 100644 std/src/sys/anonymous_pipe/tests.rs create mode 100644 std/src/sys/anonymous_pipe/unix.rs create mode 100644 std/src/sys/anonymous_pipe/unsupported.rs create mode 100644 std/src/sys/anonymous_pipe/windows.rs create mode 100644 std/tests/pipe_subprocess.rs diff --git a/std/Cargo.toml b/std/Cargo.toml index b991b1cf22dd8..321d73b76a138 100644 --- a/std/Cargo.toml +++ b/std/Cargo.toml @@ -20,8 +20,12 @@ core = { path = "../core", public = true } compiler_builtins = { version = "0.1.105" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } -hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep-of-std'] } -std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] } +hashbrown = { version = "0.14", default-features = false, features = [ + 'rustc-dep-of-std', +] } +std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = [ + 'rustc-dep-of-std', +] } # Dependencies of the `backtrace` crate rustc-demangle = { version = "0.1.24", features = ['rustc-dep-of-std'] } @@ -31,13 +35,27 @@ miniz_oxide = { version = "0.7.0", optional = true, default-features = false } addr2line = { version = "0.22.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true } +libc = { version = "0.2.153", default-features = false, features = [ + 'rustc-dep-of-std', +], public = true } [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies] -object = { version = "0.36.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } +object = { version = "0.36.0", default-features = false, optional = true, features = [ + 'read_core', + 'elf', + 'macho', + 'pe', + 'unaligned', + 'archive', +] } [target.'cfg(target_os = "aix")'.dependencies] -object = { version = "0.36.0", default-features = false, optional = true, features = ['read_core', 'xcoff', 'unaligned', 'archive'] } +object = { version = "0.36.0", default-features = false, optional = true, features = [ + 'read_core', + 'xcoff', + 'unaligned', + 'archive', +] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } @@ -47,13 +65,19 @@ rand_xorshift = "0.3.0" dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] } [target.x86_64-fortanix-unknown-sgx.dependencies] -fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public = true } +fortanix-sgx-abi = { version = "0.5.0", features = [ + 'rustc-dep-of-std', +], public = true } [target.'cfg(target_os = "hermit")'.dependencies] -hermit-abi = { version = "0.4.0", features = ['rustc-dep-of-std'], public = true } +hermit-abi = { version = "0.4.0", features = [ + 'rustc-dep-of-std', +], public = true } [target.'cfg(target_os = "wasi")'.dependencies] -wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } +wasi = { version = "0.11.0", features = [ + 'rustc-dep-of-std', +], default-features = false } [target.'cfg(target_os = "uefi")'.dependencies] r-efi = { version = "4.2.0", features = ['rustc-dep-of-std'] } @@ -61,9 +85,9 @@ r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std'] } [features] backtrace = [ - 'addr2line/rustc-dep-of-std', - 'object/rustc-dep-of-std', - 'miniz_oxide/rustc-dep-of-std', + 'addr2line/rustc-dep-of-std', + 'object/rustc-dep-of-std', + 'miniz_oxide/rustc-dep-of-std', ] panic-unwind = ["panic_unwind"] @@ -77,7 +101,10 @@ llvm-libunwind = ["unwind/llvm-libunwind"] system-llvm-libunwind = ["unwind/system-llvm-libunwind"] # Make panics and failed asserts immediately abort without formatting any message -panic_immediate_abort = ["core/panic_immediate_abort", "alloc/panic_immediate_abort"] +panic_immediate_abort = [ + "core/panic_immediate_abort", + "alloc/panic_immediate_abort", +] # Choose algorithms that are optimized for binary size instead of runtime performance optimize_for_size = ["core/optimize_for_size", "alloc/optimize_for_size"] @@ -97,6 +124,11 @@ threads = 125 # Maximum heap size heap_size = 0x8000000 +[[test]] +name = "pipe-subprocess" +path = "tests/pipe_subprocess.rs" +harness = false + [[bench]] name = "stdbenches" path = "benches/lib.rs" diff --git a/std/src/lib.rs b/std/src/lib.rs index f0a73a308a4a4..0fd2edc21acbd 100644 --- a/std/src/lib.rs +++ b/std/src/lib.rs @@ -590,6 +590,8 @@ pub mod panic; #[unstable(feature = "core_pattern_types", issue = "none")] pub mod pat; pub mod path; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub mod pipe; pub mod process; pub mod sync; pub mod time; diff --git a/std/src/pipe.rs b/std/src/pipe.rs new file mode 100644 index 0000000000000..f251b57a7cca6 --- /dev/null +++ b/std/src/pipe.rs @@ -0,0 +1,130 @@ +//! Module for anonymous pipe +//! +//! ``` +//! #![feature(anonymous_pipe)] +//! +//! # #[cfg(miri)] fn main() {} +//! # #[cfg(not(miri))] +//! # fn main() -> std::io::Result<()> { +//! let (reader, writer) = std::pipe::pipe()?; +//! # Ok(()) +//! # } +//! ``` + +use crate::{ + io, + sys::anonymous_pipe::{pipe as pipe_inner, AnonPipe}, +}; + +/// Create anonymous pipe that is close-on-exec and blocking. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[inline] +pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) +} + +/// Read end of the anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeReader(pub(crate) AnonPipe); + +/// Write end of the anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeWriter(pub(crate) AnonPipe); + +impl PipeReader { + /// Create a new [`PipeReader`] instance that shares the same underlying file description. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +impl PipeWriter { + /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for &PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for &PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} diff --git a/std/src/sys/anonymous_pipe/mod.rs b/std/src/sys/anonymous_pipe/mod.rs new file mode 100644 index 0000000000000..74875677cf3e7 --- /dev/null +++ b/std/src/sys/anonymous_pipe/mod.rs @@ -0,0 +1,18 @@ +cfg_if::cfg_if! { + if #[cfg(unix)] { + mod unix; + pub(crate) use unix::{AnonPipe, pipe}; + + #[cfg(all(test, not(miri)))] + mod tests; + } else if #[cfg(windows)] { + mod windows; + pub(crate) use windows::{AnonPipe, pipe}; + + #[cfg(all(test, not(miri)))] + mod tests; + } else { + mod unsupported; + pub(crate) use unsupported::{AnonPipe, pipe}; + } +} diff --git a/std/src/sys/anonymous_pipe/tests.rs b/std/src/sys/anonymous_pipe/tests.rs new file mode 100644 index 0000000000000..f5ea583eefe0d --- /dev/null +++ b/std/src/sys/anonymous_pipe/tests.rs @@ -0,0 +1,20 @@ +use crate::{ + io::{Read, Write}, + pipe::pipe, +}; + +#[test] +fn pipe_creation_clone_and_rw() { + let (rx, tx) = pipe().unwrap(); + + tx.try_clone().unwrap().write_all(b"12345").unwrap(); + drop(tx); + + let mut rx2 = rx.try_clone().unwrap(); + drop(rx); + + let mut s = String::new(); + rx2.read_to_string(&mut s).unwrap(); + drop(rx2); + assert_eq!(s, "12345"); +} diff --git a/std/src/sys/anonymous_pipe/unix.rs b/std/src/sys/anonymous_pipe/unix.rs new file mode 100644 index 0000000000000..ddbf1d7334fe0 --- /dev/null +++ b/std/src/sys/anonymous_pipe/unix.rs @@ -0,0 +1,103 @@ +use crate::{ + io, + os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}, + pipe::{PipeReader, PipeWriter}, + process::Stdio, + sys::{fd::FileDesc, pipe::anon_pipe}, + sys_common::{FromInner, IntoInner}, +}; + +pub(crate) type AnonPipe = FileDesc; + +#[inline] +pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsFd for PipeReader { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawFd for PipeReader { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedFd { + fn from(pipe: PipeReader) -> Self { + FileDesc::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawFd for PipeReader { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FileDesc::from_raw_fd(raw_fd)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawFd for PipeReader { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeReader) -> Self { + Self::from(OwnedFd::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsFd for PipeWriter { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawFd for PipeWriter { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedFd { + fn from(pipe: PipeWriter) -> Self { + FileDesc::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawFd for PipeWriter { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FileDesc::from_raw_fd(raw_fd)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawFd for PipeWriter { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeWriter) -> Self { + Self::from(OwnedFd::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeReader { + fn from(owned_fd: OwnedFd) -> Self { + Self(FileDesc::from_inner(owned_fd)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeWriter { + fn from(owned_fd: OwnedFd) -> Self { + Self(FileDesc::from_inner(owned_fd)) + } +} diff --git a/std/src/sys/anonymous_pipe/unsupported.rs b/std/src/sys/anonymous_pipe/unsupported.rs new file mode 100644 index 0000000000000..5962b69203ee2 --- /dev/null +++ b/std/src/sys/anonymous_pipe/unsupported.rs @@ -0,0 +1,26 @@ +use crate::{ + io, + pipe::{PipeReader, PipeWriter}, + process::Stdio, +}; + +pub(crate) use crate::sys::pipe::AnonPipe; + +#[inline] +pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + Err(io::Error::UNSUPPORTED_PLATFORM) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeReader) -> Self { + pipe.0.diverge() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeWriter) -> Self { + pipe.0.diverge() + } +} diff --git a/std/src/sys/anonymous_pipe/windows.rs b/std/src/sys/anonymous_pipe/windows.rs new file mode 100644 index 0000000000000..81f95aa286a9c --- /dev/null +++ b/std/src/sys/anonymous_pipe/windows.rs @@ -0,0 +1,109 @@ +use crate::{ + io, + os::windows::io::{ + AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, + }, + pipe::{PipeReader, PipeWriter}, + process::Stdio, + sys::{handle::Handle, pipe::unnamed_anon_pipe}, + sys_common::{FromInner, IntoInner}, +}; + +pub(crate) type AnonPipe = Handle; + +#[inline] +pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + unnamed_anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsHandle for PipeReader { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.as_handle() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawHandle for PipeReader { + fn as_raw_handle(&self) -> RawHandle { + self.0.as_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawHandle for PipeReader { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self(Handle::from_raw_handle(raw_handle)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawHandle for PipeReader { + fn into_raw_handle(self) -> RawHandle { + self.0.into_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedHandle { + fn from(pipe: PipeReader) -> Self { + Handle::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeReader) -> Self { + Self::from(OwnedHandle::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsHandle for PipeWriter { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.as_handle() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawHandle for PipeWriter { + fn as_raw_handle(&self) -> RawHandle { + self.0.as_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawHandle for PipeWriter { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self(Handle::from_raw_handle(raw_handle)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawHandle for PipeWriter { + fn into_raw_handle(self) -> RawHandle { + self.0.into_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedHandle { + fn from(pipe: PipeWriter) -> Self { + Handle::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeWriter) -> Self { + Self::from(OwnedHandle::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeReader { + fn from(owned_handle: OwnedHandle) -> Self { + Self(Handle::from_inner(owned_handle)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeWriter { + fn from(owned_handle: OwnedHandle) -> Self { + Self(Handle::from_inner(owned_handle)) + } +} diff --git a/std/src/sys/mod.rs b/std/src/sys/mod.rs index e50758ce00d8b..202997b749513 100644 --- a/std/src/sys/mod.rs +++ b/std/src/sys/mod.rs @@ -7,6 +7,8 @@ mod pal; mod personality; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; pub mod exit_guard; diff --git a/std/src/sys/pal/unix/fd.rs b/std/src/sys/pal/unix/fd.rs index 1701717db597c..10ae3c3ab570d 100644 --- a/std/src/sys/pal/unix/fd.rs +++ b/std/src/sys/pal/unix/fd.rs @@ -82,6 +82,11 @@ const fn max_iov() -> usize { } impl FileDesc { + #[inline] + pub fn try_clone(&self) -> io::Result { + self.duplicate() + } + pub fn read(&self, buf: &mut [u8]) -> io::Result { let ret = cvt(unsafe { libc::read( diff --git a/std/src/sys/pal/unix/pipe.rs b/std/src/sys/pal/unix/pipe.rs index 33db24e77e4da..8762af614f17e 100644 --- a/std/src/sys/pal/unix/pipe.rs +++ b/std/src/sys/pal/unix/pipe.rs @@ -9,6 +9,7 @@ use crate::sys_common::{FromInner, IntoInner}; // Anonymous pipes //////////////////////////////////////////////////////////////////////////////// +#[derive(Debug)] pub struct AnonPipe(FileDesc); pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { @@ -46,6 +47,10 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { } impl AnonPipe { + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(Self) + } + pub fn read(&self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } @@ -79,6 +84,10 @@ impl AnonPipe { pub fn is_write_vectored(&self) -> bool { self.0.is_write_vectored() } + + pub fn as_file_desc(&self) -> &FileDesc { + &self.0 + } } impl IntoInner for AnonPipe { diff --git a/std/src/sys/pal/unsupported/pipe.rs b/std/src/sys/pal/unsupported/pipe.rs index d7d8f297ae586..781eafe2f1a6b 100644 --- a/std/src/sys/pal/unsupported/pipe.rs +++ b/std/src/sys/pal/unsupported/pipe.rs @@ -1,8 +1,21 @@ -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::{ + fmt, + io::{self, BorrowedCursor, IoSlice, IoSliceMut}, +}; pub struct AnonPipe(!); +impl fmt::Debug for AnonPipe { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + impl AnonPipe { + pub fn try_clone(&self) -> io::Result { + self.0 + } + pub fn read(&self, _buf: &mut [u8]) -> io::Result { self.0 } diff --git a/std/src/sys/pal/windows/c/bindings.txt b/std/src/sys/pal/windows/c/bindings.txt index 5ad4a3731d822..7423bbe769432 100644 --- a/std/src/sys/pal/windows/c/bindings.txt +++ b/std/src/sys/pal/windows/c/bindings.txt @@ -2462,6 +2462,7 @@ Windows.Win32.System.LibraryLoader.GetProcAddress Windows.Win32.System.Performance.QueryPerformanceCounter Windows.Win32.System.Performance.QueryPerformanceFrequency Windows.Win32.System.Pipes.CreateNamedPipeW +Windows.Win32.System.Pipes.CreatePipe Windows.Win32.System.Pipes.NAMED_PIPE_MODE Windows.Win32.System.Pipes.PIPE_ACCEPT_REMOTE_CLIENTS Windows.Win32.System.Pipes.PIPE_CLIENT_END diff --git a/std/src/sys/pal/windows/c/windows_sys.rs b/std/src/sys/pal/windows/c/windows_sys.rs index fea00fec9ae59..91c7d7ebc5e0d 100644 --- a/std/src/sys/pal/windows/c/windows_sys.rs +++ b/std/src/sys/pal/windows/c/windows_sys.rs @@ -14,6 +14,7 @@ windows_targets::link!("kernel32.dll" "system" fn CreateEventW(lpeventattributes windows_targets::link!("kernel32.dll" "system" fn CreateFileW(lpfilename : PCWSTR, dwdesiredaccess : u32, dwsharemode : FILE_SHARE_MODE, lpsecurityattributes : *const SECURITY_ATTRIBUTES, dwcreationdisposition : FILE_CREATION_DISPOSITION, dwflagsandattributes : FILE_FLAGS_AND_ATTRIBUTES, htemplatefile : HANDLE) -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn CreateHardLinkW(lpfilename : PCWSTR, lpexistingfilename : PCWSTR, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CreateNamedPipeW(lpname : PCWSTR, dwopenmode : FILE_FLAGS_AND_ATTRIBUTES, dwpipemode : NAMED_PIPE_MODE, nmaxinstances : u32, noutbuffersize : u32, ninbuffersize : u32, ndefaulttimeout : u32, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn CreatePipe(hreadpipe : *mut HANDLE, hwritepipe : *mut HANDLE, lppipeattributes : *const SECURITY_ATTRIBUTES, nsize : u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CreateProcessW(lpapplicationname : PCWSTR, lpcommandline : PWSTR, lpprocessattributes : *const SECURITY_ATTRIBUTES, lpthreadattributes : *const SECURITY_ATTRIBUTES, binherithandles : BOOL, dwcreationflags : PROCESS_CREATION_FLAGS, lpenvironment : *const core::ffi::c_void, lpcurrentdirectory : PCWSTR, lpstartupinfo : *const STARTUPINFOW, lpprocessinformation : *mut PROCESS_INFORMATION) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CreateSymbolicLinkW(lpsymlinkfilename : PCWSTR, lptargetfilename : PCWSTR, dwflags : SYMBOLIC_LINK_FLAGS) -> BOOLEAN); windows_targets::link!("kernel32.dll" "system" fn CreateThread(lpthreadattributes : *const SECURITY_ATTRIBUTES, dwstacksize : usize, lpstartaddress : LPTHREAD_START_ROUTINE, lpparameter : *const core::ffi::c_void, dwcreationflags : THREAD_CREATION_FLAGS, lpthreadid : *mut u32) -> HANDLE); diff --git a/std/src/sys/pal/windows/handle.rs b/std/src/sys/pal/windows/handle.rs index aaa1831dcc24d..706062ab984e7 100644 --- a/std/src/sys/pal/windows/handle.rs +++ b/std/src/sys/pal/windows/handle.rs @@ -17,6 +17,7 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; /// An owned container for `HANDLE` object, closing them on Drop. /// /// All methods are inherited through a `Deref` impl to `RawHandle` +#[derive(Debug)] pub struct Handle(OwnedHandle); impl Handle { @@ -136,6 +137,12 @@ impl Handle { } } + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut me = self; + + Read::read_to_end(&mut me, buf) + } + pub unsafe fn read_overlapped( &self, buf: &mut [mem::MaybeUninit], diff --git a/std/src/sys/pal/windows/pipe.rs b/std/src/sys/pal/windows/pipe.rs index 7a309b9638bd2..d5e2356116f22 100644 --- a/std/src/sys/pal/windows/pipe.rs +++ b/std/src/sys/pal/windows/pipe.rs @@ -1,7 +1,7 @@ use crate::os::windows::prelude::*; use crate::ffi::OsStr; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::mem; use crate::path::Path; use crate::ptr; @@ -39,6 +39,23 @@ pub struct Pipes { pub theirs: AnonPipe, } +/// Create true unnamed anonymous pipe. +pub fn unnamed_anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { + let mut read_pipe = c::INVALID_HANDLE_VALUE; + let mut write_pipe = c::INVALID_HANDLE_VALUE; + + let ret = unsafe { c::CreatePipe(&mut read_pipe, &mut write_pipe, ptr::null_mut(), 0) }; + + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(( + AnonPipe::from_inner(unsafe { Handle::from_raw_handle(read_pipe) }), + AnonPipe::from_inner(unsafe { Handle::from_raw_handle(write_pipe) }), + )) + } +} + /// Although this looks similar to `anon_pipe` in the Unix module it's actually /// subtly different. Here we'll return two pipes in the `Pipes` return value, /// but one is intended for "us" where as the other is intended for "someone @@ -181,7 +198,7 @@ pub fn spawn_pipe_relay( their_handle_inheritable: bool, ) -> io::Result { // We need this handle to live for the lifetime of the thread spawned below. - let source = source.duplicate()?; + let source = source.try_clone()?; // create a new pair of anon pipes. let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?; @@ -237,7 +254,8 @@ impl AnonPipe { pub fn into_handle(self) -> Handle { self.inner } - fn duplicate(&self) -> io::Result { + + pub fn try_clone(&self) -> io::Result { self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner }) } diff --git a/std/tests/pipe_subprocess.rs b/std/tests/pipe_subprocess.rs new file mode 100644 index 0000000000000..c2278098b9b3f --- /dev/null +++ b/std/tests/pipe_subprocess.rs @@ -0,0 +1,39 @@ +#![feature(anonymous_pipe)] + +fn main() { + #[cfg(all(not(miri), any(unix, windows)))] + { + use std::{env, io::Read, pipe::pipe, process}; + + if env::var("I_AM_THE_CHILD").is_ok() { + child(); + } else { + parent(); + } + + fn parent() { + let me = env::current_exe().unwrap(); + + let (rx, tx) = pipe().unwrap(); + assert!( + process::Command::new(me) + .env("I_AM_THE_CHILD", "1") + .stdout(tx) + .status() + .unwrap() + .success() + ); + + let mut s = String::new(); + (&rx).read_to_string(&mut s).unwrap(); + drop(rx); + assert_eq!(s, "Heloo,\n"); + + println!("Test pipe_subprocess.rs success"); + } + + fn child() { + println!("Heloo,"); + } + } +}