From 7c408f11dce86870a0bff17a82f346de864536bb Mon Sep 17 00:00:00 2001 From: jtnunley Date: Thu, 11 Aug 2022 13:47:00 -0700 Subject: [PATCH 1/6] add I/O safe traits --- .github/workflows/ci.yml | 15 ++++++++++ Cargo.toml | 1 + src/fs/file.rs | 60 +++++++++++++++++++++++++++++++++++-- src/io/stderr.rs | 20 +++++++++++++ src/io/stdin.rs | 20 +++++++++++++ src/io/stdout.rs | 20 +++++++++++++ src/net/tcp/listener.rs | 44 +++++++++++++++++++++++++++ src/net/tcp/stream.rs | 44 +++++++++++++++++++++++++++ src/net/udp/mod.rs | 44 +++++++++++++++++++++++++++ src/os/unix/io.rs | 4 +++ src/os/unix/net/datagram.rs | 22 ++++++++++++++ src/os/unix/net/listener.rs | 22 ++++++++++++++ src/os/unix/net/stream.rs | 22 ++++++++++++++ src/os/windows/io.rs | 7 +++++ src/utils.rs | 12 ++++++++ 15 files changed, 355 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a717826f..90c2d7f5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,6 +108,21 @@ jobs: with: command: check args: --all --features tokio02 + + check_io_safety_feature: + name: Check io_safety feature + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v3 + - name: check io_safety + uses: actions-rs/cargo@v1 + with: + command: check + args: --all --features io_safety + cross: name: Cross compile diff --git a/Cargo.toml b/Cargo.toml index 9a7a3fe2a..c9947686a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ alloc = [ tokio1 = ["async-global-executor/tokio"] tokio02 = ["async-global-executor/tokio02"] tokio03 = ["async-global-executor/tokio03"] +io_safety = [] [dependencies] async-attributes = { version = "1.1.1", optional = true } diff --git a/src/fs/file.rs b/src/fs/file.rs index 6fb7ad59e..dc6e5bcc1 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -415,6 +415,8 @@ impl From for File { cfg_unix! { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + + impl AsRawFd for File { fn as_raw_fd(&self) -> RawFd { self.file.as_raw_fd() @@ -432,10 +434,36 @@ cfg_unix! { let file = self.file.clone(); drop(self); Arc::try_unwrap(file) - .expect("cannot acquire ownership of the file handle after drop") + .expect(ARC_TRY_UNWRAP_EXPECT) .into_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for File { + fn as_fd(&self) -> BorrowedFd<'_> { + self.file.as_fd() + } + } + + impl From for File { + fn from(fd: OwnedFd) -> Self { + std::fs::File::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(val: File) -> OwnedFd { + let file = val.file.clone(); + drop(val); + Arc::try_unwrap(file) + .expect(ARC_TRY_UNWRAP_EXPECT) + .into() + } + } + } } cfg_windows! { @@ -458,10 +486,36 @@ cfg_windows! { let file = self.file.clone(); drop(self); Arc::try_unwrap(file) - .expect("cannot acquire ownership of the file handle after drop") + .expect(ARC_TRY_UNWRAP_EXPECT) .into_raw_handle() } } + + cfg_io_safety! { + use crate::os::windows::io::{AsHandle, BorrowedHandle, OwnedHandle}; + + impl AsHandle for File { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.file.as_handle() + } + } + + impl From for File { + fn from(handle: OwnedHandle) -> Self { + std::fs::File::from(handle).into() + } + } + + impl From for OwnedHandle { + fn from(val: File) -> OwnedHandle { + let file = val.file.clone(); + drop(val); + Arc::try_unwrap(file) + .expect(ARC_TRY_UNWRAP_EXPECT) + .into() + } + } + } } /// An async mutex with non-borrowing lock guards. @@ -974,3 +1028,5 @@ mod tests { }) } } + +const ARC_TRY_UNWRAP_EXPECT: &str = "cannot acquire ownership of the file handle after drop"; \ No newline at end of file diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 22dadd1f6..3f38e8dea 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -180,6 +180,16 @@ cfg_unix! { std::io::stderr().as_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd}; + + impl AsFd for Stderr { + fn as_fd(&self) -> BorrowedFd<'_> { + std::io::stderr().as_fd() + } + } + } } cfg_windows! { @@ -190,4 +200,14 @@ cfg_windows! { std::io::stderr().as_raw_handle() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsHandle, BorrowedHandle}; + + impl AsHandle for Stderr { + fn as_handle(&self) -> BorrowedHandle<'_> { + std::io::stderr().as_handle() + } + } + } } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index c969574e5..52c31f110 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -206,6 +206,16 @@ cfg_unix! { std::io::stdin().as_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd}; + + impl AsFd for Stderr { + fn as_fd(&self) -> BorrowedFd<'_> { + std::io::stdin().as_fd() + } + } + } } cfg_windows! { @@ -216,4 +226,14 @@ cfg_windows! { std::io::stdin().as_raw_handle() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd}; + + impl AsFd for Stdin { + fn as_fd(&self) -> BorrowedFd<'_> { + std::io::stdin().as_fd() + } + } + } } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 45244b140..c1cd2bcfb 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -180,6 +180,16 @@ cfg_unix! { std::io::stdout().as_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd}; + + impl AsFd for Stdout { + fn as_fd(&self) -> BorrowedFd<'_> { + std::io::stdout().as_fd() + } + } + } } cfg_windows! { @@ -190,4 +200,14 @@ cfg_windows! { std::io::stdout().as_raw_handle() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsHandle, BorrowedHandle}; + + impl AsHandle for Stdout { + fn as_handle(&self) -> BorrowedHandle<'_> { + std::io::stdout().as_handle() + } + } + } } diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 69340db4e..d014f3387 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -282,6 +282,28 @@ cfg_unix! { self.watcher.into_inner().unwrap().into_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for TcpListener { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for TcpListener { + fn from(fd: OwnedFd) -> TcpListener { + std::net::TcpListener::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(listener: TcpListener) -> OwnedFd { + listener.watcher.into_inner().unwrap().into() + } + } + } } cfg_windows! { @@ -306,4 +328,26 @@ cfg_windows! { self.watcher.into_inner().unwrap().into_raw_socket() } } + + cfg_io_safety! { + use crate::os::windows::io::{AsSocket, BorrowedSocket, OwnedSocket}; + + impl AsSocket for TcpListener { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.watcher.get_ref().as_socket() + } + } + + impl From for TcpListener { + fn from(fd: OwnedSocket) -> TcpListener { + std::net::TcpListener::from(fd).into() + } + } + + impl From for OwnedSocket { + fn from(listener: TcpListener) -> OwnedSocket { + listener.watcher.into_inner().unwrap().into() + } + } + } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index f30e4714c..955df82d0 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -416,6 +416,28 @@ cfg_unix! { self.as_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for TcpStream { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for TcpStream { + fn from(fd: OwnedFd) -> TcpStream { + std::net::TcpStream::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: TcpStream) -> OwnedFd { + stream.watcher.into_inner().unwrap().into() + } + } + } } cfg_windows! { @@ -443,4 +465,26 @@ cfg_windows! { self.as_raw_socket() } } + + cfg_io_safety! { + use crate::os::windows::io::{AsSocket, BorrowedSocket, OwnedSocket}; + + impl AsSocket for TcpStream { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.watcher.get_ref().as_socket() + } + } + + impl From for TcpStream { + fn from(fd: OwnedSocket) -> TcpStream { + std::net::TcpListener::from(fd).into() + } + } + + impl From for OwnedSocket { + fn from(stream: TcpStream) -> OwnedSocket { + stream.watcher.into_inner().unwrap().into() + } + } + } } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index e216af43a..90fdf3e25 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -562,6 +562,28 @@ cfg_unix! { self.watcher.into_inner().unwrap().into_raw_fd() } } + + cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for UdpSocket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for UdpSocket { + fn from(fd: OwnedFd) -> UdpSocket { + std::net::TcpStream::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: UdpSocket) -> OwnedFd { + stream.watcher.into_inner().unwrap().into() + } + } + } } cfg_windows! { @@ -586,4 +608,26 @@ cfg_windows! { self.watcher.into_inner().unwrap().into_raw_socket() } } + + cfg_io_safety! { + use crate::os::windows::io::{AsSocket, BorrowedSocket, OwnedSocket}; + + impl AsSocket for UdpSocket { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.watcher.get_ref().as_socket() + } + } + + impl From for UdpSocket { + fn from(fd: OwnedSocket) -> UdpSocket { + std::net::TcpListener::from(fd).into() + } + } + + impl From for OwnedSocket { + fn from(stream: UdpSocket) -> OwnedSocket { + stream.watcher.into_inner().unwrap().into() + } + } + } } diff --git a/src/os/unix/io.rs b/src/os/unix/io.rs index 0b9846074..a7708eee3 100644 --- a/src/os/unix/io.rs +++ b/src/os/unix/io.rs @@ -2,6 +2,10 @@ cfg_not_docs! { pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + + cfg_io_safety! { + pub use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + } } cfg_docs! { diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 7b0fc32ec..3a92c78b7 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -340,3 +340,25 @@ impl IntoRawFd for UnixDatagram { self.watcher.into_inner().unwrap().into_raw_fd() } } + +cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for UnixDatagram { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for UnixDatagram { + fn from(fd: OwnedFd) -> UnixDatagram { + std::net::TcpStream::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: UnixDatagram) -> OwnedFd { + stream.watcher.into_inner().unwrap().into() + } + } +} \ No newline at end of file diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 1f983656f..48a6e36d7 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -233,3 +233,25 @@ impl IntoRawFd for UnixListener { self.watcher.into_inner().unwrap().into_raw_fd() } } + +cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for UnixListener { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for UnixListener { + fn from(fd: OwnedFd) -> UnixListener { + std::net::TcpStream::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: UnixListener) -> OwnedFd { + stream.watcher.into_inner().unwrap().into() + } + } +} \ No newline at end of file diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 8674e7c32..9a2387b10 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -264,3 +264,25 @@ impl IntoRawFd for UnixStream { (*self.watcher).get_ref().try_clone().unwrap().into_raw_fd() } } + +cfg_io_safety! { + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + + impl AsFd for UnixStream { + fn as_fd(&self) -> BorrowedFd<'_> { + self.watcher.get_ref().as_fd() + } + } + + impl From for UnixStream { + fn from(fd: OwnedFd) -> UnixStream { + std::net::TcpStream::from(fd).into() + } + } + + impl From for OwnedFd { + fn from(stream: UnixStream) -> OwnedFd { + stream.watcher.into_inner().unwrap().into() + } + } +} \ No newline at end of file diff --git a/src/os/windows/io.rs b/src/os/windows/io.rs index 30d37a0ef..5fea53dc4 100644 --- a/src/os/windows/io.rs +++ b/src/os/windows/io.rs @@ -5,6 +5,13 @@ cfg_not_docs! { AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket, }; + + cfg_io_safety! { + pub use std::os::windows::io::{ + AsHandle, BorrowedHandle, OwnedHandle, + AsSocket, BorrowedSocket, OwnedSocket, + }; + } } cfg_docs! { diff --git a/src/utils.rs b/src/utils.rs index ab451a660..e541b82e6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -239,3 +239,15 @@ macro_rules! cfg_default { )* } } + +/// Declares items that use I/O safety. +#[allow(unused_macros)] +#[doc(hidden)] +macro_rules! cfg_io_safety { + ($($item:item)*) => { + $( + #[cfg(feature = "io-safety")] + $item + )* + } +} From e1d66f53a2a0e05da27c4417f85b371d2a9070df Mon Sep 17 00:00:00 2001 From: jtnunley Date: Tue, 16 Aug 2022 07:04:04 -0700 Subject: [PATCH 2/6] code review --- src/fs/file.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index dc6e5bcc1..0dba3da97 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -15,6 +15,8 @@ use crate::prelude::*; use crate::task::{spawn_blocking, Context, Poll, Waker}; use crate::utils::Context as _; +const ARC_TRY_UNWRAP_EXPECT: &str = "cannot acquire ownership of the file handle after drop"; + /// An open file on the filesystem. /// /// Depending on what options the file was opened with, this type can be used for reading and/or @@ -413,9 +415,7 @@ impl From for File { } cfg_unix! { - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - - + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; impl AsRawFd for File { fn as_raw_fd(&self) -> RawFd { @@ -1027,6 +1027,4 @@ mod tests { Ok(()) }) } -} - -const ARC_TRY_UNWRAP_EXPECT: &str = "cannot acquire ownership of the file handle after drop"; \ No newline at end of file +} \ No newline at end of file From c63c43341a678a42963b8e4019abc5b636a4f26c Mon Sep 17 00:00:00 2001 From: jtnunley Date: Tue, 16 Aug 2022 07:05:47 -0700 Subject: [PATCH 3/6] add end-of-file newline --- src/fs/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 0dba3da97..e0ac42904 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -1027,4 +1027,4 @@ mod tests { Ok(()) }) } -} \ No newline at end of file +} From 27f26ea430337ae15ceed87ba2b4947772d2338b Mon Sep 17 00:00:00 2001 From: jtnunley Date: Tue, 16 Aug 2022 07:15:59 -0700 Subject: [PATCH 4/6] iline docs --- src/os/unix/io.rs | 5 +++++ src/os/windows/io.rs | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/os/unix/io.rs b/src/os/unix/io.rs index a7708eee3..9d1fbaede 100644 --- a/src/os/unix/io.rs +++ b/src/os/unix/io.rs @@ -55,4 +55,9 @@ cfg_docs! { /// and must close the descriptor once it's no longer needed. fn into_raw_fd(self) -> RawFd; } + + cfg_io_safety! { + #[doc(inline)] + pub use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; + } } diff --git a/src/os/windows/io.rs b/src/os/windows/io.rs index 5fea53dc4..caffc6fc6 100644 --- a/src/os/windows/io.rs +++ b/src/os/windows/io.rs @@ -82,4 +82,12 @@ cfg_docs! { /// it once it's no longer needed. fn into_raw_socket(self) -> RawSocket; } + + cfg_io_safety! { + #[doc(inline)] + pub use std::os::windows::io::{ + AsHandle, BorrowedHandle, OwnedHandle, + AsSocket, BorrowedSocket, OwnedSocket, + }; + } } From dc7bb8e97d805c6fe26f2f2e6cf8a821a437e0d6 Mon Sep 17 00:00:00 2001 From: jtnunley Date: Sun, 30 Apr 2023 10:00:35 -0700 Subject: [PATCH 5/6] Fix minor issues --- src/fs/file.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index e0ac42904..db80ee7e3 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -415,7 +415,16 @@ impl From for File { } cfg_unix! { - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + + impl File { + fn into_std_file(self) -> std::fs::File { + let file = self.file.clone(); + drop(self); + Arc::try_unwrap(file) + .expect(ARC_TRY_UNWRAP_EXPECT) + } + } impl AsRawFd for File { fn as_raw_fd(&self) -> RawFd { @@ -431,11 +440,7 @@ cfg_unix! { impl IntoRawFd for File { fn into_raw_fd(self) -> RawFd { - let file = self.file.clone(); - drop(self); - Arc::try_unwrap(file) - .expect(ARC_TRY_UNWRAP_EXPECT) - .into_raw_fd() + self.into_std_file().into_raw_fd() } } @@ -456,11 +461,7 @@ cfg_unix! { impl From for OwnedFd { fn from(val: File) -> OwnedFd { - let file = val.file.clone(); - drop(val); - Arc::try_unwrap(file) - .expect(ARC_TRY_UNWRAP_EXPECT) - .into() + self.into_std_file() } } } From c17710366b3f5222dd7cda985b8392347a126c38 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Mon, 1 May 2023 07:05:30 -0700 Subject: [PATCH 6/6] Update src/fs/file.rs Co-authored-by: Josh Triplett --- src/fs/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index db80ee7e3..67dfbe7f5 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -461,7 +461,7 @@ cfg_unix! { impl From for OwnedFd { fn from(val: File) -> OwnedFd { - self.into_std_file() + self.into_std_file().into() } } }