From c0e799db1a0acfd638a8a165eb703766b683ff37 Mon Sep 17 00:00:00 2001 From: tiif Date: Sun, 18 Aug 2024 01:57:24 +0800 Subject: [PATCH 1/3] Set EINVAL for epoll_wait maxevent value 0 --- src/tools/miri/src/shims/unix/linux/epoll.rs | 3 ++- src/tools/miri/tests/pass-dep/libc/libc-epoll.rs | 15 +++++++++++++++ 2 files changed, 17 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 127817d5bbe3b..c69a2ac216e6d 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -409,11 +409,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; 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)); } + // FIXME: Implement blocking support if timeout != 0 { throw_unsup_format!("epoll_wait: timeout value can only be 0"); 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 71f0e832afb11..eb742f2ccfb0a 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_less_maxevent_zero(); } // Using `as` cast since `EPOLLET` wraps around @@ -528,3 +529,17 @@ 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_less_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); +} From 8ae118dc0a9115c6a2783fd42a236d54fab15417 Mon Sep 17 00:00:00 2001 From: tiif Date: Sun, 18 Aug 2024 13:36:56 +0800 Subject: [PATCH 2/3] Move the maxevents.try_into().unwrap() after value check --- src/tools/miri/src/shims/unix/linux/epoll.rs | 12 +++++++----- src/tools/miri/tests/pass-dep/libc/libc-epoll.rs | 5 ++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index c69a2ac216e6d..9ee2ad9625772 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -403,18 +403,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let epfd = this.read_scalar(epfd)?.to_i32()?; 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 || 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 event = this.deref_pointer_as( + events_op, + 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"); 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 eb742f2ccfb0a..773204c49476d 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll.rs @@ -19,7 +19,7 @@ fn main() { test_epoll_ctl_del(); test_pointer(); test_two_same_fd_in_same_epoll_instance(); - test_epoll_wait_less_maxevent_zero(); + test_epoll_wait_maxevent_zero(); } // Using `as` cast since `EPOLLET` wraps around @@ -530,8 +530,7 @@ fn test_no_notification_for_unregister_flag() { check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]); } - -fn test_epoll_wait_less_maxevent_zero() { +fn test_epoll_wait_maxevent_zero() { // Create an epoll instance. let epfd = unsafe { libc::epoll_create1(0) }; assert_ne!(epfd, -1); From f918de8c4a268fb5632c56cafecff0e602010e67 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 18 Aug 2024 10:41:58 +0200 Subject: [PATCH 3/3] make sure we read all arguments before returning early --- src/tools/miri/src/shims/unix/linux/epoll.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index 9ee2ad9625772..3af7c188dfcd3 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -402,8 +402,10 @@ 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 timeout = this.read_scalar(timeout)?.to_i32()?; + if epfd <= 0 || maxevents <= 0 { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; @@ -412,8 +414,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This needs to come after the maxevents value check, or else maxevents.try_into().unwrap() // will fail. - let event = this.deref_pointer_as( - events_op, + let events = this.deref_pointer_as( + &events, this.libc_array_ty_layout("epoll_event", maxevents.try_into().unwrap()), )?; @@ -432,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