about summary refs log tree commit diff
path: root/library/std/src/os/unix/io
diff options
context:
space:
mode:
authorDan Gohman <dev@sunfishcode.online>2021-06-30 21:44:30 -0700
committerDan Gohman <dev@sunfishcode.online>2021-08-19 12:02:39 -0700
commitd15418586ca78ead4f87ad18fcffa3550c1b169e (patch)
tree93234d47e5eab3c9177ad31b9369abcd1c1ed5d1 /library/std/src/os/unix/io
parent2451f42c1deb9379d5e8e5fa86b0bf857ae048ec (diff)
downloadrust-d15418586ca78ead4f87ad18fcffa3550c1b169e.tar.gz
rust-d15418586ca78ead4f87ad18fcffa3550c1b169e.zip
I/O safety.
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:
 - <https://github.com/rust-lang/rust/issues/87074>

RFC:
 - <https://github.com/rust-lang/rfcs/blob/master/text/3128-io-safety.md>
Diffstat (limited to 'library/std/src/os/unix/io')
-rw-r--r--library/std/src/os/unix/io/fd.rs329
-rw-r--r--library/std/src/os/unix/io/mod.rs56
-rw-r--r--library/std/src/os/unix/io/raw.rs195
3 files changed, 580 insertions, 0 deletions
diff --git a/library/std/src/os/unix/io/fd.rs b/library/std/src/os/unix/io/fd.rs
new file mode 100644
index 00000000000..5bf6369122b
--- /dev/null
+++ b/library/std/src/os/unix/io/fd.rs
@@ -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()
+    }
+}
diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs
new file mode 100644
index 00000000000..06f47cbb31f
--- /dev/null
+++ b/library/std/src/os/unix/io/mod.rs
@@ -0,0 +1,56 @@
+//! Unix-specific extensions to general I/O primitives.
+//!
+//! Just like raw pointers, raw file descriptors point to resources with
+//! dynamic lifetimes, and they can dangle if they outlive their resources
+//! or be forged if they're created from invalid values.
+//!
+//! This module provides three types for representing file descriptors,
+//! with different ownership properties: raw, borrowed, and owned, which are
+//! analogous to types used for representing pointers:
+//!
+//! | Type               | Analogous to |
+//! | ------------------ | ------------ |
+//! | [`RawFd`]          | `*const _`   |
+//! | [`BorrowedFd<'a>`] | `&'a _`      |
+//! | [`OwnedFd`]        | `Box<_>`     |
+//!
+//! Like raw pointers, `RawFd` values are primitive values. And in new code,
+//! they should be considered unsafe to do I/O on (analogous to dereferencing
+//! them). Rust did not always provide this guidance, so existing code in the
+//! Rust ecosystem often doesn't mark `RawFd` usage as unsafe. Libraries are
+//! encouraged to migrate, either by adding `unsafe` to APIs that dereference
+//! `RawFd` values, or by using to `BorrowedFd` or `OwnedFd` instead.
+//!
+//! Like references, `BorrowedFd` values are tied to a lifetime, to ensure
+//! that they don't outlive the resource they point to. These are safe to
+//! use. `BorrowedFd` values may be used in APIs which provide safe access to
+//! any system call except for:
+//!  - `close`, because that would end the dynamic lifetime of the resource
+//!    without ending the lifetime of the file descriptor.
+//!  - `dup2`/`dup3`, in the second argument, because this argument is
+//!    closed and replaced with a new resource, which may break the assumptions
+//!    other code using that file descriptor.
+//! This list doesn't include `mmap`, since `mmap` does do a proper borrow of
+//! its file descriptor argument. That said, `mmap` is unsafe for other
+//! reasons: it operates on raw pointers, and it has undefined behavior if the
+//! underlying storage is mutated. Mutations may come from other processes, or
+//! from the same process if the API provides `BorrowedFd` access, since as
+//! mentioned earlier, `BorrowedFd` values may be used in APIs which provide
+//! safe access to any system call. Consequently, code using `mmap` and
+//! presenting a safe API must take full responsibility for ensuring that safe
+//! Rust code cannot evoke undefined behavior through it.
+//!
+//! Like boxes, `OwnedFd` values conceptually own the resource they point to,
+//! and free (close) it when they are dropped.
+//!
+//! [`BorrowedFd<'a>`]: crate::os::unix::io::BorrowedFd
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+mod fd;
+mod raw;
+
+#[unstable(feature = "io_safety", issue = "87074")]
+pub use fd::*;
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use raw::*;
diff --git a/library/std/src/os/unix/io/raw.rs b/library/std/src/os/unix/io/raw.rs
new file mode 100644
index 00000000000..78ee706d41d
--- /dev/null
+++ b/library/std/src/os/unix/io/raw.rs
@@ -0,0 +1,195 @@
+//! Unix-specific extensions to general I/O primitives.
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use crate::fs;
+use crate::io;
+use crate::os::raw;
+use crate::os::unix::io::OwnedFd;
+use crate::sys_common::{AsInner, IntoInner};
+
+/// Raw file descriptors.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub type RawFd = raw::c_int;
+
+/// A trait to extract the raw unix 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 `AsRawHandle`
+/// and `AsRawSocket` set of traits.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait AsRawFd {
+    /// Extracts the raw file descriptor.
+    ///
+    /// This method does **not** pass ownership of the raw file descriptor
+    /// to the caller. The descriptor is only guaranteed to be valid while
+    /// the original object has not yet been destroyed.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use std::fs::File;
+    /// # use std::io;
+    /// use std::os::unix::io::{AsRawFd, RawFd};
+    ///
+    /// let mut f = File::open("foo.txt")?;
+    /// // Note that `raw_fd` is only valid as long as `f` exists.
+    /// let raw_fd: RawFd = f.as_raw_fd();
+    /// # Ok::<(), io::Error>(())
+    /// ```
+    #[stable(feature = "rust1", since = "1.0.0")]
+    fn as_raw_fd(&self) -> RawFd;
+}
+
+/// A trait to express the ability to construct an object from a raw file
+/// descriptor.
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+pub trait FromRawFd {
+    /// Constructs a new instance of `Self` from the given raw file
+    /// descriptor.
+    ///
+    /// This function **consumes ownership** of the specified file
+    /// descriptor. The returned object will take responsibility for closing
+    /// it when the object goes out of scope.
+    ///
+    /// This function is also unsafe as the primitives currently returned
+    /// have the contract that they are the sole owner of the file
+    /// descriptor they are wrapping. Usage of this function could
+    /// accidentally allow violating this contract which can cause memory
+    /// unsafety in code that relies on it being true.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use std::fs::File;
+    /// # use std::io;
+    /// use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd};
+    ///
+    /// let f = File::open("foo.txt")?;
+    /// let raw_fd: RawFd = f.into_raw_fd();
+    /// // SAFETY: no other functions should call `from_raw_fd`, so there
+    /// // is only one owner for the file descriptor.
+    /// let f = unsafe { File::from_raw_fd(raw_fd) };
+    /// # Ok::<(), io::Error>(())
+    /// ```
+    #[stable(feature = "from_raw_os", since = "1.1.0")]
+    unsafe fn from_raw_fd(fd: RawFd) -> Self;
+}
+
+/// A trait to express the ability to consume an object and acquire ownership of
+/// its raw file descriptor.
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+pub trait IntoRawFd {
+    /// Consumes this object, returning the raw underlying file descriptor.
+    ///
+    /// This function **transfers ownership** of the underlying file descriptor
+    /// to the caller. Callers are then the unique owners of the file descriptor
+    /// and must close the descriptor once it's no longer needed.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use std::fs::File;
+    /// # use std::io;
+    /// use std::os::unix::io::{IntoRawFd, RawFd};
+    ///
+    /// let f = File::open("foo.txt")?;
+    /// let raw_fd: RawFd = f.into_raw_fd();
+    /// # Ok::<(), io::Error>(())
+    /// ```
+    #[stable(feature = "into_raw_os", since = "1.4.0")]
+    fn into_raw_fd(self) -> RawFd;
+}
+
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl AsRawFd for RawFd {
+    #[inline]
+    fn as_raw_fd(&self) -> RawFd {
+        *self
+    }
+}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl IntoRawFd for RawFd {
+    #[inline]
+    fn into_raw_fd(self) -> RawFd {
+        self
+    }
+}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl FromRawFd for RawFd {
+    #[inline]
+    unsafe fn from_raw_fd(fd: RawFd) -> RawFd {
+        fd
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl AsRawFd for fs::File {
+    #[inline]
+    fn as_raw_fd(&self) -> RawFd {
+        self.as_inner().as_raw_fd()
+    }
+}
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+impl FromRawFd for fs::File {
+    #[inline]
+    unsafe fn from_raw_fd(fd: RawFd) -> fs::File {
+        fs::File::from(OwnedFd::from_raw_fd(fd))
+    }
+}
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for fs::File {
+    #[inline]
+    fn into_raw_fd(self) -> RawFd {
+        self.into_inner().into_inner().into_raw_fd()
+    }
+}
+
+#[stable(feature = "asraw_stdio", since = "1.21.0")]
+impl AsRawFd for io::Stdin {
+    #[inline]
+    fn as_raw_fd(&self) -> RawFd {
+        libc::STDIN_FILENO
+    }
+}
+
+#[stable(feature = "asraw_stdio", since = "1.21.0")]
+impl AsRawFd for io::Stdout {
+    #[inline]
+    fn as_raw_fd(&self) -> RawFd {
+        libc::STDOUT_FILENO
+    }
+}
+
+#[stable(feature = "asraw_stdio", since = "1.21.0")]
+impl AsRawFd for io::Stderr {
+    #[inline]
+    fn as_raw_fd(&self) -> RawFd {
+        libc::STDERR_FILENO
+    }
+}
+
+#[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
+impl<'a> AsRawFd for io::StdinLock<'a> {
+    #[inline]
+    fn as_raw_fd(&self) -> RawFd {
+        libc::STDIN_FILENO
+    }
+}
+
+#[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
+impl<'a> AsRawFd for io::StdoutLock<'a> {
+    #[inline]
+    fn as_raw_fd(&self) -> RawFd {
+        libc::STDOUT_FILENO
+    }
+}
+
+#[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
+impl<'a> AsRawFd for io::StderrLock<'a> {
+    #[inline]
+    fn as_raw_fd(&self) -> RawFd {
+        libc::STDERR_FILENO
+    }
+}