From 09511078810a88116fad8fcc7a6e84ce182b825b Mon Sep 17 00:00:00 2001 From: tiif Date: Sat, 17 Aug 2024 02:08:31 +0800 Subject: [PATCH 1/3] Add epoll EPOLLHUP flag support --- src/tools/miri/src/shims/unix/linux/epoll.rs | 20 +++++++++++++++++-- src/tools/miri/src/shims/unix/socket.rs | 5 +++-- .../miri/tests/pass-dep/libc/libc-epoll.rs | 6 ++++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index d529a9b63e68..a47d9fe0826d 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -69,17 +69,21 @@ 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. + 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 +95,9 @@ impl EpollReadyEvents { if self.epollrdhup { bitmask |= epollrdhup; } + if self.epollhup { + bitmask |= epollhup; + } bitmask } } @@ -217,6 +224,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 +252,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 +277,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}", diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index 75d4d0dbbe49..cf3f65f69b21 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -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(); @@ -76,6 +76,7 @@ impl FileDescription for SocketPair { // for read and write. epoll_ready_events.epollin = true; epoll_ready_events.epollout = true; + epoll_ready_events.epollhup = true; } Ok(epoll_ready_events) } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll.rs index 841761e53dd5..8d7d559c6aa3 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll.rs @@ -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)])); } From f9a7d6ec6fefb417a59347aeb81e7b3eec09a3e7 Mon Sep 17 00:00:00 2001 From: tiif Date: Sat, 17 Aug 2024 12:02:23 +0800 Subject: [PATCH 2/3] Move epoll_ready_events.epollhup = true up --- src/tools/miri/src/shims/unix/socket.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index cf3f65f69b21..f7c168c58b97 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -71,12 +71,12 @@ impl FileDescription for SocketPair { } else { // Peer FD has been closed. 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. epoll_ready_events.epollin = true; epoll_ready_events.epollout = true; - epoll_ready_events.epollhup = true; } Ok(epoll_ready_events) } From cab81d3fa96268472a121d8920fc59a9caa483df Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 17 Aug 2024 10:36:46 +0200 Subject: [PATCH 3/3] extend comments on HUP vs RDHUP --- src/tools/miri/src/shims/unix/linux/epoll.rs | 3 +++ src/tools/miri/src/shims/unix/socket.rs | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index a47d9fe0826d..53f27868aeb9 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -71,6 +71,9 @@ pub struct EpollReadyEvents { 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, } diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index f7c168c58b97..3f2adb6e79c4 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -69,7 +69,8 @@ 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