Skip to content

Commit 4df7dab

Browse files
committed
feat: I/O safety for 'poll'
1 parent 3d3e6b9 commit 4df7dab

File tree

2 files changed

+38
-18
lines changed

2 files changed

+38
-18
lines changed

src/poll.rs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! Wait for events to trigger on specific file descriptors
2-
use std::os::unix::io::{AsRawFd, RawFd};
2+
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd};
33

44
use crate::errno::Errno;
55
use crate::Result;
@@ -21,10 +21,24 @@ pub struct PollFd {
2121
impl PollFd {
2222
/// Creates a new `PollFd` specifying the events of interest
2323
/// for a given file descriptor.
24-
pub const fn new(fd: RawFd, events: PollFlags) -> PollFd {
24+
//
25+
// Different from other I/O-safe interfaces, here, we have to take `AsFd`
26+
// by reference to prevent the case where the `fd` is closed but it is
27+
// still in use. For example:
28+
//
29+
// ```rust
30+
// let file: File = File::open("some_file").unwrap();
31+
//
32+
// // If `PollFd::new()` takes `AsFd` by value, then `file` will be consumed,
33+
// // but the internal file descriptor of `file` will still be in use.
34+
// let pollfd = PollFd::new(file, flag);
35+
//
36+
// // Do something with `pollfd`, which uses the CLOSED fd.
37+
// ```
38+
pub fn new<Fd: AsFd>(fd: &Fd, events: PollFlags) -> PollFd {
2539
PollFd {
2640
pollfd: libc::pollfd {
27-
fd,
41+
fd: fd.as_fd().as_raw_fd(),
2842
events: events.bits(),
2943
revents: PollFlags::empty().bits(),
3044
},
@@ -68,9 +82,17 @@ impl PollFd {
6882
}
6983
}
7084

71-
impl AsRawFd for PollFd {
72-
fn as_raw_fd(&self) -> RawFd {
73-
self.pollfd.fd
85+
impl AsFd for PollFd {
86+
fn as_fd(&self) -> BorrowedFd<'_> {
87+
// BorrowedFd::borrow_raw() requires that the raw fd being passed must
88+
// remain open for the duration of the returned BorrowedFd, this is
89+
// guaranteed as the returned BorrowedFd has lifetime parameter same
90+
// as `self`:
91+
// "fn as_fd<'self>(&'self self) -> BorrowedFd<'self>"
92+
// which means that `self` (PollFd) is guaranteed to outlive the returned
93+
// BorrowedFd. Since `self` is valid, the fd within `self` is valid,
94+
// then the returned BorrowedFd is valid.
95+
unsafe { BorrowedFd::borrow_raw(self.pollfd.fd) }
7496
}
7597
}
7698

test/test_poll.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use nix::{
22
errno::Errno,
33
poll::{poll, PollFd, PollFlags},
4-
unistd::{pipe, write},
4+
unistd::{close, pipe, write},
55
};
6+
use std::os::unix::io::{BorrowedFd, FromRawFd, OwnedFd};
67

78
macro_rules! loop_while_eintr {
89
($poll_expr: expr) => {
@@ -19,7 +20,8 @@ macro_rules! loop_while_eintr {
1920
#[test]
2021
fn test_poll() {
2122
let (r, w) = pipe().unwrap();
22-
let mut fds = [PollFd::new(r, PollFlags::POLLIN)];
23+
let r = unsafe { OwnedFd::from_raw_fd(r) };
24+
let mut fds = [PollFd::new(&r, PollFlags::POLLIN)];
2325

2426
// Poll an idle pipe. Should timeout
2527
let nfds = loop_while_eintr!(poll(&mut fds, 100));
@@ -32,6 +34,7 @@ fn test_poll() {
3234
let nfds = poll(&mut fds, 100).unwrap();
3335
assert_eq!(nfds, 1);
3436
assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN));
37+
close(w).unwrap();
3538
}
3639

3740
// ppoll(2) is the same as poll except for how it handles timeouts and signals.
@@ -51,7 +54,8 @@ fn test_ppoll() {
5154

5255
let timeout = TimeSpec::milliseconds(1);
5356
let (r, w) = pipe().unwrap();
54-
let mut fds = [PollFd::new(r, PollFlags::POLLIN)];
57+
let r = unsafe { OwnedFd::from_raw_fd(r) };
58+
let mut fds = [PollFd::new(&r, PollFlags::POLLIN)];
5559

5660
// Poll an idle pipe. Should timeout
5761
let sigset = SigSet::empty();
@@ -65,19 +69,13 @@ fn test_ppoll() {
6569
let nfds = ppoll(&mut fds, Some(timeout), None).unwrap();
6670
assert_eq!(nfds, 1);
6771
assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN));
68-
}
69-
70-
#[test]
71-
fn test_pollfd_fd() {
72-
use std::os::unix::io::AsRawFd;
73-
74-
let pfd = PollFd::new(0x1234, PollFlags::empty());
75-
assert_eq!(pfd.as_raw_fd(), 0x1234);
72+
close(w).unwrap();
7673
}
7774

7875
#[test]
7976
fn test_pollfd_events() {
80-
let mut pfd = PollFd::new(-1, PollFlags::POLLIN);
77+
let fd_zero = unsafe { BorrowedFd::borrow_raw(0) };
78+
let mut pfd = PollFd::new(&fd_zero, PollFlags::POLLIN);
8179
assert_eq!(pfd.events(), PollFlags::POLLIN);
8280
pfd.set_events(PollFlags::POLLOUT);
8381
assert_eq!(pfd.events(), PollFlags::POLLOUT);

0 commit comments

Comments
 (0)