diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs index 3080d5b8d07..cae5abca3bd 100644 --- a/src/tools/miri/src/shims/unix/linux/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs @@ -1,6 +1,7 @@ //! Linux `eventfd` implementation. use std::io; use std::io::{Error, ErrorKind}; +use std::mem; use rustc_target::abi::Endian; @@ -9,8 +10,8 @@ use self::shims::unix::fd::FileDescriptor; -/// Minimum size of u8 array to hold u64 value. -const U64_MIN_ARRAY_SIZE: usize = 8; +// We'll only do reads and writes in chunks of size u64. +const U64_ARRAY_SIZE: usize = mem::size_of::(); /// Maximum value that the eventfd counter can hold. const MAX_COUNTER: u64 = u64::MAX - 1; @@ -51,7 +52,7 @@ fn read<'tcx>( ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result> { // Check the size of slice, and return error only if the size of the slice < 8. - let Some(bytes) = bytes.first_chunk_mut::() else { + let Some(bytes) = bytes.first_chunk_mut::() else { return Ok(Err(Error::from(ErrorKind::InvalidInput))); }; // Block when counter == 0. @@ -63,7 +64,7 @@ fn read<'tcx>( throw_unsup_format!("eventfd: blocking is unsupported"); } } else { - // Prevent false alarm in data race detection when doing synchronisation via eventfd. + // Synchronize with all prior `write` calls to this FD. ecx.acquire_clock(&self.clock); // Return the counter in the host endianness using the buffer provided by caller. *bytes = match ecx.tcx.sess.target.endian { @@ -71,7 +72,7 @@ fn read<'tcx>( Endian::Big => self.counter.to_be_bytes(), }; self.counter = 0; - return Ok(Ok(U64_MIN_ARRAY_SIZE)); + return Ok(Ok(U64_ARRAY_SIZE)); } } @@ -94,7 +95,7 @@ fn write<'tcx>( ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result> { // Check the size of slice, and return error only if the size of the slice < 8. - let Some(bytes) = bytes.first_chunk::() else { + let Some(bytes) = bytes.first_chunk::() else { return Ok(Err(Error::from(ErrorKind::InvalidInput))); }; // Convert from bytes to int according to host endianness. @@ -110,8 +111,10 @@ fn write<'tcx>( // Else, block. match self.counter.checked_add(num) { Some(new_count @ 0..=MAX_COUNTER) => { - // Prevent false alarm in data race detection when doing synchronisation via eventfd. - self.clock.join(&ecx.release_clock().unwrap()); + // Future `read` calls will synchronize with this write, so update the FD clock. + if let Some(clock) = &ecx.release_clock() { + self.clock.join(clock); + } self.counter = new_count; } None | Some(u64::MAX) => { @@ -123,7 +126,7 @@ fn write<'tcx>( } } }; - Ok(Ok(U64_MIN_ARRAY_SIZE)) + Ok(Ok(U64_ARRAY_SIZE)) } } @@ -163,9 +166,10 @@ fn eventfd(&mut self, val: &OpTy<'tcx>, flags: &OpTy<'tcx>) -> InterpResult<'tcx } let mut is_nonblock = false; - // Unload the flag that we support. + // Unset the flag that we support. // After unloading, flags != 0 means other flags are used. if flags & efd_cloexec == efd_cloexec { + // cloexec is ignored because Miri does not support exec. flags &= !efd_cloexec; } if flags & efd_nonblock == efd_nonblock { @@ -173,9 +177,7 @@ fn eventfd(&mut self, val: &OpTy<'tcx>, flags: &OpTy<'tcx>) -> InterpResult<'tcx is_nonblock = true; } if flags != 0 { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; - return Ok(Scalar::from_i32(-1)); + throw_unsup_format!("eventfd: encountered unknown unsupported flags {:#x}", flags); } let fd = this.machine.fds.insert_fd(FileDescriptor::new(Event { diff --git a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs index b24635f9340..fb9a23206c6 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs @@ -1,5 +1,4 @@ -//@ignore-target-windows: No eventfd on Windows -//@ignore-target-apple: No eventfd in macos +//@only-target-linux fn main() { // eventfd read will block when EFD_NONBLOCK flag is clear and counter = 0. // This will pass when blocking is implemented. diff --git a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs index 32ca4a919f7..2037a516dea 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs @@ -1,5 +1,4 @@ -//@ignore-target-windows: No eventfd on Windows -//@ignore-target-apple: No eventfd in macos +//@only-target-linux fn main() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs index 6af195316e8..a3567eeb7cb 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs @@ -1,5 +1,4 @@ -//@ignore-target-windows: No eventfd in windows -//@ignore-target-apple: No eventfd in macos +//@only-target-linux // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-preemption-rate=0 @@ -42,9 +41,11 @@ fn test_read_write() { // value -1. let mut buf: [u8; 8] = [0; 8]; let res = read_bytes(fd, &mut buf); + let e = std::io::Error::last_os_error(); + assert_eq!(e.raw_os_error(), Some(libc::EAGAIN)); assert_eq!(res, -1); - // Write with supplied buffer that > 8 bytes should be allowed. + // Write with supplied buffer bigger than 8 bytes should be allowed. let sized_9_data: [u8; 9]; if cfg!(target_endian = "big") { // Adjust the data based on the endianness of host system. @@ -55,19 +56,23 @@ fn test_read_write() { let res = write_bytes(fd, sized_9_data); assert_eq!(res, 8); - // Read with supplied buffer that < 8 bytes should fail with return + // Read with supplied buffer smaller than 8 bytes should fail with return // value -1. let mut buf: [u8; 7] = [1; 7]; let res = read_bytes(fd, &mut buf); + let e = std::io::Error::last_os_error(); + assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); assert_eq!(res, -1); - // Write with supplied buffer that < 8 bytes should fail with return + // Write with supplied buffer smaller than 8 bytes should fail with return // value -1. let size_7_data: [u8; 7] = [1; 7]; let res = write_bytes(fd, size_7_data); + let e = std::io::Error::last_os_error(); + assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); assert_eq!(res, -1); - // Read with supplied buffer > 8 bytes should be allowed. + // Read with supplied buffer bigger than 8 bytes should be allowed. let mut buf: [u8; 9] = [1; 9]; let res = read_bytes(fd, &mut buf); assert_eq!(res, 8); @@ -75,6 +80,8 @@ fn test_read_write() { // Write u64::MAX should fail. let u64_max_bytes: [u8; 8] = [255; 8]; let res = write_bytes(fd, u64_max_bytes); + let e = std::io::Error::last_os_error(); + assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); assert_eq!(res, -1); }