Skip to content

Commit 42e8beb

Browse files
committed
Implement validation in TryFrom<OwnedFd> for PIpe* on unix
Signed-off-by: Jiahao XU <[email protected]>
1 parent 72bda33 commit 42e8beb

File tree

1 file changed

+51
-13
lines changed

1 file changed

+51
-13
lines changed

library/std/src/io/pipe/mod.rs

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ pub struct PipeReader(AnonPipe);
2020
#[derive(Debug)]
2121
pub struct PipeWriter(AnonPipe);
2222

23-
/// The owned fd provided is not a pipe.
24-
#[derive(Debug)]
25-
pub struct NotAPipeError;
26-
2723
impl PipeReader {
2824
/// Create a new [`PipeReader`] instance that shares the same underlying file description.
2925
pub fn try_clone(&self) -> io::Result<Self> {
@@ -89,7 +85,11 @@ mod unix {
8985
use super::*;
9086

9187
use crate::{
92-
os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd},
88+
fs::File,
89+
os::{
90+
fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd},
91+
unix::fs::FileTypeExt,
92+
},
9393
sys::{
9494
fd::FileDesc,
9595
pipe::{anon_pipe, AnonPipe},
@@ -139,23 +139,61 @@ mod unix {
139139
impl_traits!(PipeReader);
140140
impl_traits!(PipeWriter);
141141

142-
fn owned_fd_to_anon_pipe(owned_fd: OwnedFd) -> AnonPipe {
143-
AnonPipe::from_inner(FileDesc::from_inner(owned_fd))
142+
fn convert_to_pipe(owned_fd: OwnedFd) -> io::Result<AnonPipe> {
143+
let file = File::from(owned_fd);
144+
if file.metadata()?.file_type().is_fifo() {
145+
Ok(AnonPipe::from_inner(FileDesc::from_inner(OwnedFd::from(file))))
146+
} else {
147+
Err(io::Error::new(io::ErrorKind::InvalidInput, "Not a pipe"))
148+
}
149+
}
150+
151+
enum AccessMode {
152+
Readable,
153+
Writable,
154+
}
155+
156+
fn check_access_mode(pipe: AnonPipe, expected_access_mode: AccessMode) -> io::Result<AnonPipe> {
157+
let ret = unsafe { libc::fcntl(pipe.as_raw_fd(), libc::F_GETFL) };
158+
let access_mode = ret & libc::O_ACCMODE;
159+
let expected_access_mode_str = match expected_access_mode {
160+
AccessMode::Readable => "readable",
161+
AccessMode::Writable => "writable",
162+
};
163+
let expected_access_mode = match expected_access_mode {
164+
AccessMode::Readable => libc::O_RDONLY,
165+
AccessMode::Writable => libc::O_WRONLY,
166+
};
167+
168+
if ret == -1 {
169+
Err(io::Error::last_os_error())
170+
} else if access_mode == libc::O_RDWR && access_mode == expected_access_mode {
171+
Err(io::Error::new(
172+
io::ErrorKind::InvalidInput,
173+
format!("Pipe {} is not {}", pipe.as_raw_fd(), expected_access_mode_str),
174+
))
175+
} else {
176+
Ok(pipe)
177+
}
144178
}
145179

146180
impl TryFrom<OwnedFd> for PipeReader {
147-
type Error = NotAPipeError;
181+
type Error = io::Error;
148182

149183
fn try_from(owned_fd: OwnedFd) -> Result<Self, Self::Error> {
150-
Ok(Self(owned_fd_to_anon_pipe(owned_fd)))
184+
convert_to_pipe(owned_fd)
185+
.and_then(|pipe| check_access_mode(pipe, AccessMode::Readable))
186+
.map(Self)
151187
}
152188
}
153189

154190
impl TryFrom<OwnedFd> for PipeWriter {
155-
type Error = NotAPipeError;
191+
type Error = io::Error;
156192

157193
fn try_from(owned_fd: OwnedFd) -> Result<Self, Self::Error> {
158-
Ok(Self(owned_fd_to_anon_pipe(owned_fd)))
194+
convert_to_pipe(owned_fd)
195+
.and_then(|pipe| check_access_mode(pipe, AccessMode::Writable))
196+
.map(Self)
159197
}
160198
}
161199
}
@@ -225,15 +263,15 @@ mod windows {
225263
}
226264

227265
impl TryFrom<OwnedHandle> for PipeReader {
228-
type Error = NotAPipeError;
266+
type Error = io::Error;
229267

230268
fn try_from(owned_handle: OwnedHandle) -> Result<Self, Self::Error> {
231269
Ok(Self(owned_handle_to_anon_pipe(owned_handle)))
232270
}
233271
}
234272

235273
impl TryFrom<OwnedHandle> for PipeWriter {
236-
type Error = NotAPipeError;
274+
type Error = io::Error;
237275

238276
fn try_from(owned_handle: OwnedHandle) -> Result<Self, Self::Error> {
239277
Ok(Self(owned_handle_to_anon_pipe(owned_handle)))

0 commit comments

Comments
 (0)