Auto merge of #3661 - tiif:fix/eventfd, r=RalfJung
Follow up PR for eventfd shim Follow up of https://github.com/rust-lang/miri/pull/3650
This commit is contained in:
commit
ad85a20031
4 changed files with 30 additions and 23 deletions
|
@ -1,6 +1,7 @@
|
||||||
//! Linux `eventfd` implementation.
|
//! Linux `eventfd` implementation.
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{Error, ErrorKind};
|
use std::io::{Error, ErrorKind};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
use rustc_target::abi::Endian;
|
use rustc_target::abi::Endian;
|
||||||
|
|
||||||
|
@ -9,8 +10,8 @@ use crate::{concurrency::VClock, *};
|
||||||
|
|
||||||
use self::shims::unix::fd::FileDescriptor;
|
use self::shims::unix::fd::FileDescriptor;
|
||||||
|
|
||||||
/// Minimum size of u8 array to hold u64 value.
|
// We'll only do reads and writes in chunks of size u64.
|
||||||
const U64_MIN_ARRAY_SIZE: usize = 8;
|
const U64_ARRAY_SIZE: usize = mem::size_of::<u64>();
|
||||||
|
|
||||||
/// Maximum value that the eventfd counter can hold.
|
/// Maximum value that the eventfd counter can hold.
|
||||||
const MAX_COUNTER: u64 = u64::MAX - 1;
|
const MAX_COUNTER: u64 = u64::MAX - 1;
|
||||||
|
@ -51,7 +52,7 @@ impl FileDescription for Event {
|
||||||
ecx: &mut MiriInterpCx<'tcx>,
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
) -> InterpResult<'tcx, io::Result<usize>> {
|
||||||
// Check the size of slice, and return error only if the size of the slice < 8.
|
// Check the size of slice, and return error only if the size of the slice < 8.
|
||||||
let Some(bytes) = bytes.first_chunk_mut::<U64_MIN_ARRAY_SIZE>() else {
|
let Some(bytes) = bytes.first_chunk_mut::<U64_ARRAY_SIZE>() else {
|
||||||
return Ok(Err(Error::from(ErrorKind::InvalidInput)));
|
return Ok(Err(Error::from(ErrorKind::InvalidInput)));
|
||||||
};
|
};
|
||||||
// Block when counter == 0.
|
// Block when counter == 0.
|
||||||
|
@ -63,7 +64,7 @@ impl FileDescription for Event {
|
||||||
throw_unsup_format!("eventfd: blocking is unsupported");
|
throw_unsup_format!("eventfd: blocking is unsupported");
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
ecx.acquire_clock(&self.clock);
|
||||||
// Return the counter in the host endianness using the buffer provided by caller.
|
// Return the counter in the host endianness using the buffer provided by caller.
|
||||||
*bytes = match ecx.tcx.sess.target.endian {
|
*bytes = match ecx.tcx.sess.target.endian {
|
||||||
|
@ -71,7 +72,7 @@ impl FileDescription for Event {
|
||||||
Endian::Big => self.counter.to_be_bytes(),
|
Endian::Big => self.counter.to_be_bytes(),
|
||||||
};
|
};
|
||||||
self.counter = 0;
|
self.counter = 0;
|
||||||
return Ok(Ok(U64_MIN_ARRAY_SIZE));
|
return Ok(Ok(U64_ARRAY_SIZE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ impl FileDescription for Event {
|
||||||
ecx: &mut MiriInterpCx<'tcx>,
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
) -> InterpResult<'tcx, io::Result<usize>> {
|
||||||
// Check the size of slice, and return error only if the size of the slice < 8.
|
// Check the size of slice, and return error only if the size of the slice < 8.
|
||||||
let Some(bytes) = bytes.first_chunk::<U64_MIN_ARRAY_SIZE>() else {
|
let Some(bytes) = bytes.first_chunk::<U64_ARRAY_SIZE>() else {
|
||||||
return Ok(Err(Error::from(ErrorKind::InvalidInput)));
|
return Ok(Err(Error::from(ErrorKind::InvalidInput)));
|
||||||
};
|
};
|
||||||
// Convert from bytes to int according to host endianness.
|
// Convert from bytes to int according to host endianness.
|
||||||
|
@ -110,8 +111,10 @@ impl FileDescription for Event {
|
||||||
// Else, block.
|
// Else, block.
|
||||||
match self.counter.checked_add(num) {
|
match self.counter.checked_add(num) {
|
||||||
Some(new_count @ 0..=MAX_COUNTER) => {
|
Some(new_count @ 0..=MAX_COUNTER) => {
|
||||||
// Prevent false alarm in data race detection when doing synchronisation via eventfd.
|
// Future `read` calls will synchronize with this write, so update the FD clock.
|
||||||
self.clock.join(&ecx.release_clock().unwrap());
|
if let Some(clock) = &ecx.release_clock() {
|
||||||
|
self.clock.join(clock);
|
||||||
|
}
|
||||||
self.counter = new_count;
|
self.counter = new_count;
|
||||||
}
|
}
|
||||||
None | Some(u64::MAX) => {
|
None | Some(u64::MAX) => {
|
||||||
|
@ -123,7 +126,7 @@ impl FileDescription for Event {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(Ok(U64_MIN_ARRAY_SIZE))
|
Ok(Ok(U64_ARRAY_SIZE))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,9 +166,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut is_nonblock = false;
|
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.
|
// After unloading, flags != 0 means other flags are used.
|
||||||
if flags & efd_cloexec == efd_cloexec {
|
if flags & efd_cloexec == efd_cloexec {
|
||||||
|
// cloexec is ignored because Miri does not support exec.
|
||||||
flags &= !efd_cloexec;
|
flags &= !efd_cloexec;
|
||||||
}
|
}
|
||||||
if flags & efd_nonblock == efd_nonblock {
|
if flags & efd_nonblock == efd_nonblock {
|
||||||
|
@ -173,9 +177,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||||
is_nonblock = true;
|
is_nonblock = true;
|
||||||
}
|
}
|
||||||
if flags != 0 {
|
if flags != 0 {
|
||||||
let einval = this.eval_libc("EINVAL");
|
throw_unsup_format!("eventfd: encountered unknown unsupported flags {:#x}", flags);
|
||||||
this.set_last_error(einval)?;
|
|
||||||
return Ok(Scalar::from_i32(-1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let fd = this.machine.fds.insert_fd(FileDescriptor::new(Event {
|
let fd = this.machine.fds.insert_fd(FileDescriptor::new(Event {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
//@ignore-target-windows: No eventfd on Windows
|
//@only-target-linux
|
||||||
//@ignore-target-apple: No eventfd in macos
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// eventfd read will block when EFD_NONBLOCK flag is clear and counter = 0.
|
// eventfd read will block when EFD_NONBLOCK flag is clear and counter = 0.
|
||||||
// This will pass when blocking is implemented.
|
// This will pass when blocking is implemented.
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
//@ignore-target-windows: No eventfd on Windows
|
//@only-target-linux
|
||||||
//@ignore-target-apple: No eventfd in macos
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// eventfd write will block when EFD_NONBLOCK flag is clear
|
// eventfd write will block when EFD_NONBLOCK flag is clear
|
||||||
// and the addition caused counter to exceed u64::MAX - 1.
|
// and the addition caused counter to exceed u64::MAX - 1.
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
//@ignore-target-windows: No eventfd in windows
|
//@only-target-linux
|
||||||
//@ignore-target-apple: No eventfd in macos
|
|
||||||
// test_race depends on a deterministic schedule.
|
// test_race depends on a deterministic schedule.
|
||||||
//@compile-flags: -Zmiri-preemption-rate=0
|
//@compile-flags: -Zmiri-preemption-rate=0
|
||||||
|
|
||||||
|
@ -42,9 +41,11 @@ fn test_read_write() {
|
||||||
// value -1.
|
// value -1.
|
||||||
let mut buf: [u8; 8] = [0; 8];
|
let mut buf: [u8; 8] = [0; 8];
|
||||||
let res = read_bytes(fd, &mut buf);
|
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);
|
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];
|
let sized_9_data: [u8; 9];
|
||||||
if cfg!(target_endian = "big") {
|
if cfg!(target_endian = "big") {
|
||||||
// Adjust the data based on the endianness of host system.
|
// 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);
|
let res = write_bytes(fd, sized_9_data);
|
||||||
assert_eq!(res, 8);
|
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.
|
// value -1.
|
||||||
let mut buf: [u8; 7] = [1; 7];
|
let mut buf: [u8; 7] = [1; 7];
|
||||||
let res = read_bytes(fd, &mut buf);
|
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);
|
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.
|
// value -1.
|
||||||
let size_7_data: [u8; 7] = [1; 7];
|
let size_7_data: [u8; 7] = [1; 7];
|
||||||
let res = write_bytes(fd, size_7_data);
|
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);
|
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 mut buf: [u8; 9] = [1; 9];
|
||||||
let res = read_bytes(fd, &mut buf);
|
let res = read_bytes(fd, &mut buf);
|
||||||
assert_eq!(res, 8);
|
assert_eq!(res, 8);
|
||||||
|
@ -75,6 +80,8 @@ fn test_read_write() {
|
||||||
// Write u64::MAX should fail.
|
// Write u64::MAX should fail.
|
||||||
let u64_max_bytes: [u8; 8] = [255; 8];
|
let u64_max_bytes: [u8; 8] = [255; 8];
|
||||||
let res = write_bytes(fd, u64_max_bytes);
|
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);
|
assert_eq!(res, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue