Auto merge of #3814 - tiif:epollhup, r=RalfJung

Add epoll EPOLLHUP flag support

Related discussion in https://github.com/rust-lang/miri/issues/3811#issuecomment-2293854742.

This PR added support for ``EPOLLHUP`` flag.
This commit is contained in:
bors 2024-08-17 08:57:07 +00:00
commit 78dfb8a108
3 changed files with 30 additions and 7 deletions

View file

@ -69,17 +69,24 @@ pub struct EpollReadyEvents {
/// Stream socket peer closed connection, or shut down writing
/// half of connection.
pub epollrdhup: bool,
/// For stream socket, this event merely indicates that the peer
/// closed its end of the channel.
/// Unlike epollrdhup, this should only be set when the stream is fully closed.
/// epollrdhup also gets set when only the write half is closed, which is possible
/// via `shutdown(_, SHUT_WR)`.
pub epollhup: bool,
}
impl EpollReadyEvents {
pub fn new() -> Self {
EpollReadyEvents { epollin: false, epollout: false, epollrdhup: false }
EpollReadyEvents { epollin: false, epollout: false, epollrdhup: false, epollhup: false }
}
pub fn get_event_bitmask<'tcx>(&self, ecx: &MiriInterpCx<'tcx>) -> u32 {
let epollin = ecx.eval_libc_u32("EPOLLIN");
let epollout = ecx.eval_libc_u32("EPOLLOUT");
let epollrdhup = ecx.eval_libc_u32("EPOLLRDHUP");
let epollhup = ecx.eval_libc_u32("EPOLLHUP");
let mut bitmask = 0;
if self.epollin {
@ -91,6 +98,9 @@ impl EpollReadyEvents {
if self.epollrdhup {
bitmask |= epollrdhup;
}
if self.epollhup {
bitmask |= epollhup;
}
bitmask
}
}
@ -217,6 +227,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let epollout = this.eval_libc_u32("EPOLLOUT");
let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
let epollet = this.eval_libc_u32("EPOLLET");
let epollhup = this.eval_libc_u32("EPOLLHUP");
// Fail on unsupported operations.
if op & epoll_ctl_add != epoll_ctl_add
@ -244,11 +255,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if op == epoll_ctl_add || op == epoll_ctl_mod {
// Read event bitmask and data from epoll_event passed by caller.
let events = this.read_scalar(&this.project_field(&event, 0)?)?.to_u32()?;
let mut events = this.read_scalar(&this.project_field(&event, 0)?)?.to_u32()?;
let data = this.read_scalar(&this.project_field(&event, 1)?)?.to_u64()?;
// Unset the flag we support to discover if any unsupported flags are used.
let mut flags = events;
// epoll_wait(2) will always wait for epollhup; it is not
// necessary to set it in events when calling epoll_ctl().
// So we will always set this event type.
events |= epollhup;
if events & epollet != epollet {
// We only support edge-triggered notification for now.
throw_unsup_format!("epoll_ctl: epollet flag must be included.");
@ -264,6 +280,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if flags & epollrdhup == epollrdhup {
flags &= !epollrdhup;
}
if flags & epollhup == epollhup {
flags &= !epollhup;
}
if flags != 0 {
throw_unsup_format!(
"epoll_ctl: encountered unknown unsupported flags {:#x}",

View file

@ -49,8 +49,8 @@ impl FileDescription for SocketPair {
}
fn get_epoll_ready_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadyEvents> {
// We only check the status of EPOLLIN, EPOLLOUT and EPOLLRDHUP flags. If other event flags
// need to be supported in the future, the check should be added here.
// We only check the status of EPOLLIN, EPOLLOUT, EPOLLHUP and EPOLLRDHUP flags.
// If other event flags need to be supported in the future, the check should be added here.
let mut epoll_ready_events = EpollReadyEvents::new();
@ -69,8 +69,10 @@ impl FileDescription for SocketPair {
epoll_ready_events.epollout = true;
}
} else {
// Peer FD has been closed.
// Peer FD has been closed. This always sets both the RDHUP and HUP flags
// as we do not support `shutdown` that could be used to partially close the stream.
epoll_ready_events.epollrdhup = true;
epoll_ready_events.epollhup = true;
// Since the peer is closed, even if no data is available reads will return EOF and
// writes will return EPIPE. In other words, they won't block, so we mark this as ready
// for read and write.

View file

@ -97,7 +97,8 @@ fn test_epoll_socketpair() {
// Check result from epoll_wait.
// We expect to get a read, write, HUP notification from the close since closing an FD always unblocks reads and writes on its peer.
let expected_event = u32::try_from(libc::EPOLLRDHUP | libc::EPOLLIN | libc::EPOLLOUT).unwrap();
let expected_event =
u32::try_from(libc::EPOLLRDHUP | libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLHUP).unwrap();
let expected_value = u64::try_from(fds[1]).unwrap();
assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
}
@ -141,7 +142,8 @@ fn test_epoll_ctl_mod() {
assert_eq!(res, 0);
// Check result from epoll_wait.
let expected_event = u32::try_from(libc::EPOLLRDHUP | libc::EPOLLIN | libc::EPOLLOUT).unwrap();
let expected_event =
u32::try_from(libc::EPOLLRDHUP | libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLHUP).unwrap();
let expected_value = u64::try_from(fds[1]).unwrap();
assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
}