Skip to content

Commit

Permalink
I/O safety.
Browse files Browse the repository at this point in the history
Introduce `OwnedFd` and `BorrowedFd`, and the `AsFd` trait, and
implementations of `AsFd`, `From<OwnedFd>` and `From<T> for OwnedFd`
for relevant types, along with Windows counterparts for handles and
sockets.

Tracking issue:
 - <rust-lang#87074>

RFC:
 - <https://github.com/rust-lang/rfcs/blob/master/text/3128-io-safety.md>
  • Loading branch information
sunfishcode committed Aug 19, 2021
1 parent 2451f42 commit d154185
Show file tree
Hide file tree
Showing 43 changed files with 2,360 additions and 556 deletions.
1 change: 1 addition & 0 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@
#![feature(int_log)]
#![feature(into_future)]
#![feature(intra_doc_pointers)]
#![feature(io_safety)]
#![feature(iter_zip)]
#![feature(lang_items)]
#![feature(linkage)]
Expand Down
3 changes: 1 addition & 2 deletions library/std/src/net/udp/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use crate::io::ErrorKind;
use crate::net::test::{next_test_ip4, next_test_ip6};
use crate::net::*;
use crate::sync::mpsc::channel;
use crate::sys_common::AsInner;
use crate::thread;
use crate::time::{Duration, Instant};

Expand Down Expand Up @@ -173,7 +172,7 @@ fn debug() {
let socket_addr = next_test_ip4();

let udpsock = t!(UdpSocket::bind(&socket_addr));
let udpsock_inner = udpsock.0.socket().as_inner();
let udpsock_inner = udpsock.0.socket().as_raw();
let compare = format!("UdpSocket {{ addr: {:?}, {}: {:?} }}", socket_addr, name, udpsock_inner);
assert_eq!(format!("{:?}", udpsock), compare);
}
Expand Down
329 changes: 329 additions & 0 deletions library/std/src/os/unix/io/fd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,329 @@
//! Owned and borrowed file descriptors.

#![unstable(feature = "io_safety", issue = "87074")]

use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::fmt;
use crate::fs;
use crate::marker::PhantomData;
use crate::mem::forget;
use crate::os::raw;
use crate::sys_common::{AsInner, FromInner, IntoInner};

/// A borrowed file descriptor.
///
/// This has a lifetime parameter to tie it to the lifetime of something that
/// owns the file descriptor.
///
/// This uses `repr(transparent)` and has the representation of a host file
/// descriptor, so it can be used in FFI in places where a file descriptor is
/// passed as an argument, it is not captured or consumed, and it never has the
/// value `-1`.
#[derive(Copy, Clone)]
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(0)]
// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
// 32-bit c_int. Below is -2, in two's complement, but that only works out
// because c_int is 32 bits.
#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct BorrowedFd<'fd> {
raw: RawFd,
_phantom: PhantomData<&'fd OwnedFd>,
}

/// An owned file descriptor.
///
/// This closes the file descriptor on drop.
///
/// This uses `repr(transparent)` and has the representation of a host file
/// descriptor, so it can be used in FFI in places where a file descriptor is
/// passed as a consumed argument or returned as an owned value, and it never
/// has the value `-1`.
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(0)]
// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
// 32-bit c_int. Below is -2, in two's complement, but that only works out
// because c_int is 32 bits.
#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct OwnedFd {
raw: RawFd,
}

impl BorrowedFd<'_> {
/// Return a `BorrowedFd` holding the given raw file descriptor.
///
/// # Safety
///
/// The resource pointed to by `raw` must remain open for the duration of
/// the returned `BorrowedFd`, and it must not have the value `-1`.
#[inline]
#[unstable(feature = "io_safety", issue = "87074")]
pub unsafe fn borrow_raw_fd(raw: RawFd) -> Self {
assert_ne!(raw, -1_i32 as RawFd);
Self { raw, _phantom: PhantomData }
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl AsRawFd for BorrowedFd<'_> {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.raw
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl AsRawFd for OwnedFd {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.raw
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl IntoRawFd for OwnedFd {
#[inline]
fn into_raw_fd(self) -> RawFd {
let raw = self.raw;
forget(self);
raw
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl FromRawFd for OwnedFd {
/// Constructs a new instance of `Self` from the given raw file descriptor.
///
/// # Safety
///
/// The resource pointed to by `raw` must be open and suitable for assuming
/// ownership.
#[inline]
unsafe fn from_raw_fd(raw: RawFd) -> Self {
assert_ne!(raw, -1i32);
// SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
Self { raw }
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl Drop for OwnedFd {
#[inline]
fn drop(&mut self) {
unsafe {
// Note that errors are ignored when closing a file descriptor. The
// reason for this is that if an error occurs we don't actually know if
// the file descriptor was closed or not, and if we retried (for
// something like EINTR), we might close another valid file descriptor
// opened after we closed ours.
let _ = libc::close(self.raw as raw::c_int);
}
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl fmt::Debug for BorrowedFd<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BorrowedFd").field("fd", &self.raw).finish()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl fmt::Debug for OwnedFd {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OwnedFd").field("fd", &self.raw).finish()
}
}

/// A trait to borrow the file descriptor from an underlying object.
///
/// This is only available on unix platforms and must be imported in order to
/// call the method. Windows platforms have a corresponding `AsHandle` and
/// `AsSocket` set of traits.
#[unstable(feature = "io_safety", issue = "87074")]
pub trait AsFd {
/// Borrows the file descriptor.
///
/// # Example
///
/// ```rust,no_run
/// #![feature(io_safety)]
/// use std::fs::File;
/// # use std::io;
/// use std::os::unix::io::{AsFd, BorrowedFd};
///
/// let mut f = File::open("foo.txt")?;
/// let borrowed_fd: BorrowedFd<'_> = f.as_fd();
/// # Ok::<(), io::Error>(())
/// ```
#[unstable(feature = "io_safety", issue = "87074")]
fn as_fd(&self) -> BorrowedFd<'_>;
}

#[unstable(feature = "io_safety", issue = "87074")]
impl AsFd for BorrowedFd<'_> {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
*self
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl AsFd for OwnedFd {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw_fd(self.as_raw_fd()) }
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl AsFd for fs::File {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.as_inner().as_fd()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl From<fs::File> for OwnedFd {
#[inline]
fn from(file: fs::File) -> OwnedFd {
file.into_inner().into_inner().into_inner()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl From<OwnedFd> for fs::File {
#[inline]
fn from(owned_fd: OwnedFd) -> Self {
Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned_fd)))
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl AsFd for crate::net::TcpStream {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.as_inner().socket().as_fd()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl From<crate::net::TcpStream> for OwnedFd {
#[inline]
fn from(tcp_stream: crate::net::TcpStream) -> OwnedFd {
tcp_stream.into_inner().into_socket().into_inner().into_inner().into()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl From<OwnedFd> for crate::net::TcpStream {
#[inline]
fn from(owned_fd: OwnedFd) -> Self {
Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner(
owned_fd,
))))
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl AsFd for crate::net::TcpListener {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.as_inner().socket().as_fd()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl From<crate::net::TcpListener> for OwnedFd {
#[inline]
fn from(tcp_listener: crate::net::TcpListener) -> OwnedFd {
tcp_listener.into_inner().into_socket().into_inner().into_inner().into()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl From<OwnedFd> for crate::net::TcpListener {
#[inline]
fn from(owned_fd: OwnedFd) -> Self {
Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner(
owned_fd,
))))
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl AsFd for crate::net::UdpSocket {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.as_inner().socket().as_fd()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl From<crate::net::UdpSocket> for OwnedFd {
#[inline]
fn from(udp_socket: crate::net::UdpSocket) -> OwnedFd {
udp_socket.into_inner().into_socket().into_inner().into_inner().into()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl From<OwnedFd> for crate::net::UdpSocket {
#[inline]
fn from(owned_fd: OwnedFd) -> Self {
Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner(
owned_fd,
))))
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl AsFd for crate::process::ChildStdin {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.as_inner().as_fd()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl From<crate::process::ChildStdin> for OwnedFd {
#[inline]
fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd {
child_stdin.into_inner().into_inner().into_inner()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl AsFd for crate::process::ChildStdout {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.as_inner().as_fd()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl From<crate::process::ChildStdout> for OwnedFd {
#[inline]
fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd {
child_stdout.into_inner().into_inner().into_inner()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl AsFd for crate::process::ChildStderr {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.as_inner().as_fd()
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl From<crate::process::ChildStderr> for OwnedFd {
#[inline]
fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd {
child_stderr.into_inner().into_inner().into_inner()
}
}
Loading

0 comments on commit d154185

Please sign in to comment.