Skip to content

Commit c74ffd8

Browse files
committed
Auto merge of rust-lang#3820 - tiif:epoller, r=RalfJung
Add epollerr support Fixes rust-lang#3816 For socketpair, if the peer fd is closed while having data in its read buffer, ``EPOLLER`` will be thrown. This is still WIP because I am currently finding a way to check if peer fd still has something in its ``readbuf`` when it is closed and add the ``EPOLLER`` flag In ``get_epoll_ready_events``.
2 parents 9370020 + 483120d commit c74ffd8

File tree

3 files changed

+83
-6
lines changed

3 files changed

+83
-6
lines changed

src/tools/miri/src/shims/unix/linux/epoll.rs

+20-3
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,27 @@ pub struct EpollReadyEvents {
7676
/// epollrdhup also gets set when only the write half is closed, which is possible
7777
/// via `shutdown(_, SHUT_WR)`.
7878
pub epollhup: bool,
79+
/// Error condition happened on the associated file descriptor.
80+
pub epollerr: bool,
7981
}
8082

8183
impl EpollReadyEvents {
8284
pub fn new() -> Self {
83-
EpollReadyEvents { epollin: false, epollout: false, epollrdhup: false, epollhup: false }
85+
EpollReadyEvents {
86+
epollin: false,
87+
epollout: false,
88+
epollrdhup: false,
89+
epollhup: false,
90+
epollerr: false,
91+
}
8492
}
8593

8694
pub fn get_event_bitmask<'tcx>(&self, ecx: &MiriInterpCx<'tcx>) -> u32 {
8795
let epollin = ecx.eval_libc_u32("EPOLLIN");
8896
let epollout = ecx.eval_libc_u32("EPOLLOUT");
8997
let epollrdhup = ecx.eval_libc_u32("EPOLLRDHUP");
9098
let epollhup = ecx.eval_libc_u32("EPOLLHUP");
99+
let epollerr = ecx.eval_libc_u32("EPOLLERR");
91100

92101
let mut bitmask = 0;
93102
if self.epollin {
@@ -102,6 +111,9 @@ impl EpollReadyEvents {
102111
if self.epollhup {
103112
bitmask |= epollhup;
104113
}
114+
if self.epollerr {
115+
bitmask |= epollerr;
116+
}
105117
bitmask
106118
}
107119
}
@@ -229,6 +241,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
229241
let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
230242
let epollet = this.eval_libc_u32("EPOLLET");
231243
let epollhup = this.eval_libc_u32("EPOLLHUP");
244+
let epollerr = this.eval_libc_u32("EPOLLERR");
232245

233246
// Fail on unsupported operations.
234247
if op & epoll_ctl_add != epoll_ctl_add
@@ -261,10 +274,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
261274

262275
// Unset the flag we support to discover if any unsupported flags are used.
263276
let mut flags = events;
264-
// epoll_wait(2) will always wait for epollhup; it is not
277+
// epoll_wait(2) will always wait for epollhup and epollerr; it is not
265278
// necessary to set it in events when calling epoll_ctl().
266-
// So we will always set this event type.
279+
// So we will always set these two event types.
267280
events |= epollhup;
281+
events |= epollerr;
268282

269283
if events & epollet != epollet {
270284
// We only support edge-triggered notification for now.
@@ -284,6 +298,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
284298
if flags & epollhup == epollhup {
285299
flags &= !epollhup;
286300
}
301+
if flags & epollerr == epollerr {
302+
flags &= !epollerr;
303+
}
287304
if flags != 0 {
288305
throw_unsup_format!(
289306
"epoll_ctl: encountered unknown unsupported flags {:#x}",

src/tools/miri/src/shims/unix/unnamed_socket.rs

+25-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! are entirely implemented inside Miri.
33
//! We also use the same infrastructure to implement unnamed pipes.
44
5-
use std::cell::{OnceCell, RefCell};
5+
use std::cell::{Cell, OnceCell, RefCell};
66
use std::collections::VecDeque;
77
use std::io;
88
use std::io::{Error, ErrorKind, Read};
@@ -27,6 +27,10 @@ struct AnonSocket {
2727
/// writing to. This is a weak reference because the other side may be closed before us; all
2828
/// future writes will then trigger EPIPE.
2929
peer_fd: OnceCell<WeakFileDescriptionRef>,
30+
/// Indicates whether the peer has lost data when the file description is closed.
31+
/// This flag is set to `true` if the peer's `readbuf` is non-empty at the time
32+
/// of closure.
33+
peer_lost_data: Cell<bool>,
3034
is_nonblock: bool,
3135
}
3236

@@ -91,6 +95,10 @@ impl FileDescription for AnonSocket {
9195
// for read and write.
9296
epoll_ready_events.epollin = true;
9397
epoll_ready_events.epollout = true;
98+
// If there is data lost in peer_fd, set EPOLLERR.
99+
if self.peer_lost_data.get() {
100+
epoll_ready_events.epollerr = true;
101+
}
94102
}
95103
Ok(epoll_ready_events)
96104
}
@@ -101,6 +109,13 @@ impl FileDescription for AnonSocket {
101109
ecx: &mut MiriInterpCx<'tcx>,
102110
) -> InterpResult<'tcx, io::Result<()>> {
103111
if let Some(peer_fd) = self.peer_fd().upgrade() {
112+
// If the current readbuf is non-empty when the file description is closed,
113+
// notify the peer that data lost has happened in current file description.
114+
if let Some(readbuf) = &self.readbuf {
115+
if !readbuf.borrow().buf.is_empty() {
116+
peer_fd.downcast::<AnonSocket>().unwrap().peer_lost_data.set(true);
117+
}
118+
}
104119
// Notify peer fd that close has happened, since that can unblock reads and writes.
105120
ecx.check_and_update_readiness(&peer_fd)?;
106121
}
@@ -290,11 +305,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
290305
let fd0 = fds.new_ref(AnonSocket {
291306
readbuf: Some(RefCell::new(Buffer::new())),
292307
peer_fd: OnceCell::new(),
308+
peer_lost_data: Cell::new(false),
293309
is_nonblock: is_sock_nonblock,
294310
});
295311
let fd1 = fds.new_ref(AnonSocket {
296312
readbuf: Some(RefCell::new(Buffer::new())),
297313
peer_fd: OnceCell::new(),
314+
peer_lost_data: Cell::new(false),
298315
is_nonblock: is_sock_nonblock,
299316
});
300317

@@ -340,10 +357,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
340357
let fd0 = fds.new_ref(AnonSocket {
341358
readbuf: Some(RefCell::new(Buffer::new())),
342359
peer_fd: OnceCell::new(),
360+
peer_lost_data: Cell::new(false),
361+
is_nonblock: false,
362+
});
363+
let fd1 = fds.new_ref(AnonSocket {
364+
readbuf: None,
365+
peer_fd: OnceCell::new(),
366+
peer_lost_data: Cell::new(false),
343367
is_nonblock: false,
344368
});
345-
let fd1 =
346-
fds.new_ref(AnonSocket { readbuf: None, peer_fd: OnceCell::new(), is_nonblock: false });
347369

348370
// Make the file descriptions point to each other.
349371
fd0.downcast::<AnonSocket>().unwrap().peer_fd.set(fd1.downgrade()).unwrap();

src/tools/miri/tests/pass-dep/libc/libc-epoll.rs

+38
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ fn main() {
2020
test_pointer();
2121
test_two_same_fd_in_same_epoll_instance();
2222
test_epoll_wait_maxevent_zero();
23+
test_socketpair_epollerr();
2324
test_epoll_lost_events();
2425
test_ready_list_fetching_logic();
2526
}
@@ -551,6 +552,43 @@ fn test_epoll_wait_maxevent_zero() {
551552
assert_eq!(res, -1);
552553
}
553554

555+
fn test_socketpair_epollerr() {
556+
// Create an epoll instance.
557+
let epfd = unsafe { libc::epoll_create1(0) };
558+
assert_ne!(epfd, -1);
559+
560+
// Create a socketpair instance.
561+
let mut fds = [-1, -1];
562+
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
563+
assert_eq!(res, 0);
564+
565+
// Write to fd[0]
566+
let data = "abcde".as_bytes().as_ptr();
567+
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
568+
assert_eq!(res, 5);
569+
570+
// Close fds[1].
571+
// EPOLLERR will be triggered if we close peer fd that still has data in its read buffer.
572+
let res = unsafe { libc::close(fds[1]) };
573+
assert_eq!(res, 0);
574+
575+
// Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP
576+
let mut ev = libc::epoll_event {
577+
events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET | libc::EPOLLRDHUP) as _,
578+
u64: u64::try_from(fds[1]).unwrap(),
579+
};
580+
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
581+
assert_ne!(res, -1);
582+
583+
// Check result from epoll_wait.
584+
let expected_event = u32::try_from(
585+
libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLHUP | libc::EPOLLRDHUP | libc::EPOLLERR,
586+
)
587+
.unwrap();
588+
let expected_value = u64::try_from(fds[1]).unwrap();
589+
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
590+
}
591+
554592
// This is a test for https://github.com/rust-lang/miri/issues/3812,
555593
// epoll can lose events if they don't fit in the output buffer.
556594
fn test_epoll_lost_events() {

0 commit comments

Comments
 (0)