diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index 127817d5bbe3..3af7c188dfcd 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -402,18 +402,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let epfd = this.read_scalar(epfd)?.to_i32()?; + let events = this.read_immediate(events_op)?; let maxevents = this.read_scalar(maxevents)?.to_i32()?; - let event = this.deref_pointer_as( - events_op, - this.libc_array_ty_layout("epoll_event", maxevents.try_into().unwrap()), - )?; let timeout = this.read_scalar(timeout)?.to_i32()?; - if epfd <= 0 { + if epfd <= 0 || maxevents <= 0 { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; return Ok(Scalar::from_i32(-1)); } + + // This needs to come after the maxevents value check, or else maxevents.try_into().unwrap() + // will fail. + let events = this.deref_pointer_as( + &events, + this.libc_array_ty_layout("epoll_event", maxevents.try_into().unwrap()), + )?; + // FIXME: Implement blocking support if timeout != 0 { throw_unsup_format!("epoll_wait: timeout value can only be 0"); @@ -429,7 +434,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ready_list = epoll_file_description.get_ready_list(); let mut ready_list = ready_list.borrow_mut(); let mut num_of_events: i32 = 0; - let mut array_iter = this.project_array_fields(&event)?; + let mut array_iter = this.project_array_fields(&events)?; while let Some((epoll_key, epoll_return)) = ready_list.pop_first() { // If the file description is fully close, the entry for corresponding FdID in the 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 71f0e832afb1..773204c49476 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll.rs @@ -19,6 +19,7 @@ fn main() { test_epoll_ctl_del(); test_pointer(); test_two_same_fd_in_same_epoll_instance(); + test_epoll_wait_maxevent_zero(); } // Using `as` cast since `EPOLLET` wraps around @@ -528,3 +529,16 @@ fn test_no_notification_for_unregister_flag() { let expected_value = u64::try_from(fds[0]).unwrap(); check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]); } + +fn test_epoll_wait_maxevent_zero() { + // Create an epoll instance. + let epfd = unsafe { libc::epoll_create1(0) }; + assert_ne!(epfd, -1); + // It is ok to use uninitialised pointer here because it will error out before the + // pointer actually get accessed. + let array_ptr = MaybeUninit::::uninit().as_mut_ptr(); + let res = unsafe { libc::epoll_wait(epfd, array_ptr, 0, 0) }; + let e = std::io::Error::last_os_error(); + assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); + assert_eq!(res, -1); +}