Skip to content

Commit 1cb8578

Browse files
bjorn3squell
authored andcommitted
Use BorrowedFd instead of RawFd where possible
BorrowedFd ensures that the fd doesn't get closed while it is still getting used.
1 parent 42dfd33 commit 1cb8578

File tree

9 files changed

+73
-77
lines changed

9 files changed

+73
-77
lines changed

src/common/bin_serde.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use sealed::DeSerializeBytes;
33
use std::{
44
io::{self, Read, Write},
55
marker::PhantomData,
6-
os::{fd::AsRawFd, unix::net::UnixStream},
6+
os::{
7+
fd::{AsFd, BorrowedFd},
8+
unix::net::UnixStream,
9+
},
710
};
811

912
mod sealed {
@@ -83,9 +86,9 @@ impl<R: DeSerialize, W: DeSerialize> BinPipe<R, W> {
8386
}
8487
}
8588

86-
impl<R: DeSerialize, W: DeSerialize> AsRawFd for BinPipe<R, W> {
87-
fn as_raw_fd(&self) -> std::os::fd::RawFd {
88-
self.sock.as_raw_fd()
89+
impl<R: DeSerialize, W: DeSerialize> AsFd for BinPipe<R, W> {
90+
fn as_fd(&self) -> BorrowedFd {
91+
self.sock.as_fd()
8992
}
9093
}
9194

src/cutils/mod.rs

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use std::{
22
ffi::{CStr, OsStr, OsString},
3-
os::unix::prelude::OsStrExt,
3+
os::{
4+
fd::{AsRawFd, BorrowedFd},
5+
unix::prelude::OsStrExt,
6+
},
47
};
58

69
pub fn cerr<Int: Copy + TryInto<libc::c_long>>(res: Int) -> std::io::Result<Int> {
@@ -69,13 +72,13 @@ pub unsafe fn os_string_from_ptr(ptr: *const libc::c_char) -> OsString {
6972
/// Rust's standard library IsTerminal just directly calls isatty, which
7073
/// we don't want since this performs IOCTL calls on them and file descriptors are under
7174
/// the control of the user; so this checks if they are a character device first.
72-
pub fn safe_isatty(fildes: libc::c_int) -> bool {
75+
pub fn safe_isatty(fildes: BorrowedFd) -> bool {
7376
// The Rust standard library doesn't have FileTypeExt on Std{in,out,err}, so we
7477
// can't just use FileTypeExt::is_char_device and have to resort to libc::fstat.
7578
let mut maybe_stat = std::mem::MaybeUninit::<libc::stat>::uninit();
7679

7780
// SAFETY: we are passing fstat a pointer to valid memory
78-
if unsafe { libc::fstat(fildes, maybe_stat.as_mut_ptr()) } == 0 {
81+
if unsafe { libc::fstat(fildes.as_raw_fd(), maybe_stat.as_mut_ptr()) } == 0 {
7982
// SAFETY: if `fstat` returned 0, maybe_stat will be initialized
8083
let mode = unsafe { maybe_stat.assume_init() }.st_mode;
8184

@@ -84,7 +87,7 @@ pub fn safe_isatty(fildes: libc::c_int) -> bool {
8487

8588
if is_char_device {
8689
// SAFETY: isatty will return 0 or 1
87-
unsafe { libc::isatty(fildes) != 0 }
90+
unsafe { libc::isatty(fildes.as_raw_fd()) != 0 }
8891
} else {
8992
false
9093
}
@@ -115,29 +118,15 @@ mod test {
115118

116119
#[test]
117120
fn test_tty() {
121+
use crate::system::term::Pty;
118122
use std::fs::File;
119-
use std::os::fd::AsRawFd;
120-
assert!(!super::safe_isatty(
121-
File::open("/bin/sh").unwrap().as_raw_fd()
122-
));
123-
assert!(!super::safe_isatty(-837492));
124-
let (mut leader, mut follower) = Default::default();
125-
assert!(
126-
unsafe {
127-
libc::openpty(
128-
&mut leader,
129-
&mut follower,
130-
std::ptr::null_mut(),
131-
std::ptr::null_mut(),
132-
std::ptr::null_mut(),
133-
)
134-
} == 0
135-
);
136-
assert!(super::safe_isatty(leader));
137-
assert!(super::safe_isatty(follower));
138-
unsafe {
139-
libc::close(follower);
140-
libc::close(leader);
141-
}
123+
use std::os::fd::{AsFd, BorrowedFd};
124+
assert!(!super::safe_isatty(File::open("/bin/sh").unwrap().as_fd()));
125+
assert!(!super::safe_isatty(unsafe {
126+
BorrowedFd::borrow_raw(-837492)
127+
}));
128+
let pty = Pty::open().unwrap();
129+
assert!(super::safe_isatty(pty.leader.as_fd()));
130+
assert!(super::safe_isatty(pty.follower.as_fd()));
142131
}
143132
}

src/exec/event.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::{
22
fmt::Debug,
33
io,
4-
os::fd::{AsRawFd, RawFd},
4+
os::fd::{AsFd, AsRawFd, RawFd},
55
};
66

77
use libc::{c_short, pollfd, POLLIN, POLLOUT};
@@ -103,6 +103,7 @@ pub enum PollEvent {
103103
}
104104

105105
struct PollFd<T: Process> {
106+
// FIXME ensure that the fd is not closed while the event registry is still open
106107
raw_fd: RawFd,
107108
event_flags: c_short,
108109
should_poll: bool,
@@ -126,7 +127,7 @@ impl<T: Process> EventRegistry<T> {
126127

127128
/// Set the `fd` descriptor to be polled for `poll_event` events and produce `event` when `fd` is
128129
/// ready.
129-
pub(super) fn register_event<F: AsRawFd>(
130+
pub(super) fn register_event<F: AsFd>(
130131
&mut self,
131132
fd: &F,
132133
poll_event: PollEvent,
@@ -135,7 +136,7 @@ impl<T: Process> EventRegistry<T> {
135136
let id = EventId(self.poll_fds.len());
136137

137138
self.poll_fds.push(PollFd {
138-
raw_fd: fd.as_raw_fd(),
139+
raw_fd: fd.as_fd().as_raw_fd(),
139140
event_flags: match poll_event {
140141
PollEvent::Readable => POLLIN,
141142
PollEvent::Writable => POLLOUT,

src/exec/use_pty/backchannel.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{
22
ffi::c_int,
33
io,
44
mem::size_of,
5-
os::fd::{AsRawFd, RawFd},
5+
os::fd::{AsFd, BorrowedFd},
66
};
77

88
use crate::{
@@ -190,9 +190,9 @@ impl ParentBackchannel {
190190
}
191191
}
192192

193-
impl AsRawFd for ParentBackchannel {
194-
fn as_raw_fd(&self) -> RawFd {
195-
self.socket.as_raw_fd()
193+
impl AsFd for ParentBackchannel {
194+
fn as_fd(&self) -> BorrowedFd {
195+
self.socket.as_fd()
196196
}
197197
}
198198

@@ -309,8 +309,8 @@ impl MonitorBackchannel {
309309
}
310310
}
311311

312-
impl AsRawFd for MonitorBackchannel {
313-
fn as_raw_fd(&self) -> RawFd {
314-
self.socket.as_raw_fd()
312+
impl AsFd for MonitorBackchannel {
313+
fn as_fd(&self) -> BorrowedFd {
314+
self.socket.as_fd()
315315
}
316316
}

src/exec/use_pty/pipe/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ mod ring_buffer;
33
use std::{
44
io::{self, Read, Write},
55
marker::PhantomData,
6-
os::fd::AsRawFd,
6+
os::fd::AsFd,
77
};
88

99
use crate::exec::event::{EventHandle, EventRegistry, PollEvent, Process};
@@ -18,7 +18,7 @@ pub(super) struct Pipe<L, R> {
1818
buffer_rl: Buffer<R, L>,
1919
}
2020

21-
impl<L: Read + Write + AsRawFd, R: Read + Write + AsRawFd> Pipe<L, R> {
21+
impl<L: Read + Write + AsFd, R: Read + Write + AsFd> Pipe<L, R> {
2222
/// Create a new pipe between two read-write types and register them to be polled.
2323
pub fn new<T: Process>(
2424
left: L,

src/system/mod.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{
99
mem::MaybeUninit,
1010
ops,
1111
os::{
12-
fd::AsRawFd,
12+
fd::{AsFd, AsRawFd},
1313
unix::{self, prelude::OsStrExt},
1414
},
1515
path::{Path, PathBuf},
@@ -72,8 +72,8 @@ impl FileCloser {
7272
}
7373
}
7474

75-
pub(crate) fn except<F: AsRawFd>(&mut self, fd: &F) {
76-
self.fds.insert(fd.as_raw_fd() as c_uint);
75+
pub(crate) fn except<F: AsFd>(&mut self, fd: &F) {
76+
self.fds.insert(fd.as_fd().as_raw_fd() as c_uint);
7777
}
7878

7979
/// Close every file descriptor that is not one of the IO streams or one of the file
@@ -909,7 +909,10 @@ pub(crate) const ROOT_GROUP_NAME: &str = "wheel";
909909
mod tests {
910910
use std::{
911911
io::{self, Read, Write},
912-
os::{fd::AsRawFd, unix::net::UnixStream},
912+
os::{
913+
fd::{AsFd, AsRawFd},
914+
unix::net::UnixStream,
915+
},
913916
process::exit,
914917
};
915918

@@ -1093,8 +1096,8 @@ mod tests {
10931096
);
10941097
}
10951098

1096-
fn is_closed<F: AsRawFd>(fd: &F) -> bool {
1097-
crate::cutils::cerr(unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_GETFD) })
1099+
fn is_closed<F: AsFd>(fd: &F) -> bool {
1100+
crate::cutils::cerr(unsafe { libc::fcntl(fd.as_fd().as_raw_fd(), libc::F_GETFD) })
10981101
.is_err_and(|err| err.raw_os_error() == Some(libc::EBADF))
10991102
}
11001103

src/system/signal/stream.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{
22
io,
33
mem::MaybeUninit,
44
os::{
5-
fd::{AsRawFd, RawFd},
5+
fd::{AsFd, AsRawFd, BorrowedFd},
66
unix::net::UnixStream,
77
},
88
sync::OnceLock,
@@ -37,7 +37,7 @@ pub(super) unsafe fn send_siginfo(
3737
/// [`super::SignalHandlerBehavior::Stream`] behavior.
3838
///
3939
/// This is a singleton type. Meaning that there will be only one value of this type during the
40-
/// execution of a program.
40+
/// execution of a program.
4141
pub(crate) struct SignalStream {
4242
rx: UnixStream,
4343
tx: UnixStream,
@@ -105,8 +105,8 @@ pub(crate) fn register_handlers<const N: usize>(
105105
Ok(handlers.map(|(_, handler)| unsafe { handler.assume_init() }))
106106
}
107107

108-
impl AsRawFd for SignalStream {
109-
fn as_raw_fd(&self) -> RawFd {
110-
self.rx.as_raw_fd()
108+
impl AsFd for SignalStream {
109+
fn as_fd(&self) -> BorrowedFd {
110+
self.rx.as_fd()
111111
}
112112
}

src/system/term/mod.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{
55
fmt,
66
fs::File,
77
io,
8-
os::fd::{AsRawFd, FromRawFd, OwnedFd},
8+
os::fd::{AsFd, AsRawFd, FromRawFd, OwnedFd},
99
ptr::null_mut,
1010
};
1111

@@ -115,9 +115,9 @@ impl io::Write for PtyLeader {
115115
}
116116
}
117117

118-
impl AsRawFd for PtyLeader {
119-
fn as_raw_fd(&self) -> std::os::fd::RawFd {
120-
self.file.as_raw_fd()
118+
impl AsFd for PtyLeader {
119+
fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
120+
self.file.as_fd()
121121
}
122122
}
123123

@@ -131,9 +131,9 @@ impl PtyFollower {
131131
}
132132
}
133133

134-
impl AsRawFd for PtyFollower {
135-
fn as_raw_fd(&self) -> std::os::fd::RawFd {
136-
self.file.as_raw_fd()
134+
impl AsFd for PtyFollower {
135+
fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
136+
self.file.as_fd()
137137
}
138138
}
139139

@@ -144,11 +144,11 @@ impl From<PtyFollower> for std::process::Stdio {
144144
}
145145

146146
mod sealed {
147-
use std::os::fd::AsRawFd;
147+
use std::os::fd::AsFd;
148148

149149
pub(crate) trait Sealed {}
150150

151-
impl<F: AsRawFd> Sealed for F {}
151+
impl<F: AsFd> Sealed for F {}
152152
}
153153

154154
pub(crate) trait Terminal: sealed::Sealed {
@@ -160,49 +160,49 @@ pub(crate) trait Terminal: sealed::Sealed {
160160
fn tcgetsid(&self) -> io::Result<ProcessId>;
161161
}
162162

163-
impl<F: AsRawFd> Terminal for F {
163+
impl<F: AsFd> Terminal for F {
164164
/// Get the foreground process group ID associated with this terminal.
165165
fn tcgetpgrp(&self) -> io::Result<ProcessId> {
166166
// SAFETY: tcgetpgrp cannot cause UB
167-
let id = cerr(unsafe { libc::tcgetpgrp(self.as_raw_fd()) })?;
167+
let id = cerr(unsafe { libc::tcgetpgrp(self.as_fd().as_raw_fd()) })?;
168168
Ok(ProcessId::new(id))
169169
}
170170
/// Set the foreground process group ID associated with this terminal to `pgrp`.
171171
fn tcsetpgrp(&self, pgrp: ProcessId) -> io::Result<()> {
172172
// SAFETY: tcsetpgrp cannot cause UB
173-
cerr(unsafe { libc::tcsetpgrp(self.as_raw_fd(), pgrp.inner()) }).map(|_| ())
173+
cerr(unsafe { libc::tcsetpgrp(self.as_fd().as_raw_fd(), pgrp.inner()) }).map(|_| ())
174174
}
175175

176176
/// Make the given terminal the controlling terminal of the calling process.
177177
fn make_controlling_terminal(&self) -> io::Result<()> {
178178
// SAFETY: this is a correct way to call the TIOCSCTTY ioctl, see:
179179
// https://www.man7.org/linux/man-pages/man2/TIOCNOTTY.2const.html
180-
cerr(unsafe { libc::ioctl(self.as_raw_fd(), libc::TIOCSCTTY, 0) })?;
180+
cerr(unsafe { libc::ioctl(self.as_fd().as_raw_fd(), libc::TIOCSCTTY, 0) })?;
181181
Ok(())
182182
}
183183

184184
/// Get the filename of the tty
185185
fn ttyname(&self) -> io::Result<OsString> {
186186
let mut buf: [libc::c_char; 1024] = [0; 1024];
187187

188-
if !safe_isatty(self.as_raw_fd()) {
188+
if !safe_isatty(self.as_fd()) {
189189
return Err(io::ErrorKind::Unsupported.into());
190190
}
191191

192192
// SAFETY: `buf` is a valid and initialized pointer, and its correct length is passed
193-
cerr(unsafe { libc::ttyname_r(self.as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?;
193+
cerr(unsafe { libc::ttyname_r(self.as_fd().as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?;
194194
// SAFETY: `buf` will have been initialized by the `ttyname_r` call, if it succeeded
195195
Ok(unsafe { os_string_from_ptr(buf.as_ptr()) })
196196
}
197197

198198
/// Rust standard library "IsTerminal" is not secure for setuid programs (CVE-2023-2002)
199199
fn is_terminal(&self) -> bool {
200-
safe_isatty(self.as_raw_fd())
200+
safe_isatty(self.as_fd())
201201
}
202202

203203
fn tcgetsid(&self) -> io::Result<ProcessId> {
204204
// SAFETY: tcgetsid cannot cause UB
205-
let id = cerr(unsafe { libc::tcgetsid(self.as_raw_fd()) })?;
205+
let id = cerr(unsafe { libc::tcgetsid(self.as_fd().as_raw_fd()) })?;
206206
Ok(ProcessId::new(id))
207207
}
208208
}

0 commit comments

Comments
 (0)