mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Auto merge of #3852 - tiif:rwrefactor, r=RalfJung
Refactor fd read/write This PR passed the responsibility of reading to user supplied buffer and dest place to each implementation of ``FileDescription::read/write/pread/pwrite``. This is part of #3665.
This commit is contained in:
commit
599b3295f5
@ -25,49 +25,64 @@ pub(crate) enum FlockOp {
|
|||||||
pub trait FileDescription: std::fmt::Debug + Any {
|
pub trait FileDescription: std::fmt::Debug + Any {
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
||||||
/// Reads as much as possible into the given buffer, and returns the number of bytes read.
|
/// Reads as much as possible into the given buffer `ptr`.
|
||||||
|
/// `len` indicates how many bytes we should try to read.
|
||||||
|
/// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error.
|
||||||
fn read<'tcx>(
|
fn read<'tcx>(
|
||||||
&self,
|
&self,
|
||||||
_self_ref: &FileDescriptionRef,
|
_self_ref: &FileDescriptionRef,
|
||||||
_communicate_allowed: bool,
|
_communicate_allowed: bool,
|
||||||
_bytes: &mut [u8],
|
_ptr: Pointer,
|
||||||
|
_len: usize,
|
||||||
|
_dest: &MPlaceTy<'tcx>,
|
||||||
_ecx: &mut MiriInterpCx<'tcx>,
|
_ecx: &mut MiriInterpCx<'tcx>,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
) -> InterpResult<'tcx> {
|
||||||
throw_unsup_format!("cannot read from {}", self.name());
|
throw_unsup_format!("cannot read from {}", self.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes as much as possible from the given buffer, and returns the number of bytes written.
|
/// Writes as much as possible from the given buffer `ptr`.
|
||||||
|
/// `len` indicates how many bytes we should try to write.
|
||||||
|
/// `dest` is where the return value should be stored: number of bytes written, or `-1` in case of error.
|
||||||
fn write<'tcx>(
|
fn write<'tcx>(
|
||||||
&self,
|
&self,
|
||||||
_self_ref: &FileDescriptionRef,
|
_self_ref: &FileDescriptionRef,
|
||||||
_communicate_allowed: bool,
|
_communicate_allowed: bool,
|
||||||
_bytes: &[u8],
|
_ptr: Pointer,
|
||||||
|
_len: usize,
|
||||||
|
_dest: &MPlaceTy<'tcx>,
|
||||||
_ecx: &mut MiriInterpCx<'tcx>,
|
_ecx: &mut MiriInterpCx<'tcx>,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
) -> InterpResult<'tcx> {
|
||||||
throw_unsup_format!("cannot write to {}", self.name());
|
throw_unsup_format!("cannot write to {}", self.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads as much as possible into the given buffer from a given offset,
|
/// Reads as much as possible into the given buffer `ptr` from a given offset.
|
||||||
/// and returns the number of bytes read.
|
/// `len` indicates how many bytes we should try to read.
|
||||||
|
/// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error.
|
||||||
fn pread<'tcx>(
|
fn pread<'tcx>(
|
||||||
&self,
|
&self,
|
||||||
_communicate_allowed: bool,
|
_communicate_allowed: bool,
|
||||||
_bytes: &mut [u8],
|
|
||||||
_offset: u64,
|
_offset: u64,
|
||||||
|
_ptr: Pointer,
|
||||||
|
_len: usize,
|
||||||
|
_dest: &MPlaceTy<'tcx>,
|
||||||
_ecx: &mut MiriInterpCx<'tcx>,
|
_ecx: &mut MiriInterpCx<'tcx>,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
) -> InterpResult<'tcx> {
|
||||||
throw_unsup_format!("cannot pread from {}", self.name());
|
throw_unsup_format!("cannot pread from {}", self.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes as much as possible from the given buffer starting at a given offset,
|
/// Writes as much as possible from the given buffer `ptr` starting at a given offset.
|
||||||
/// and returns the number of bytes written.
|
/// `ptr` is the pointer to the user supplied read buffer.
|
||||||
|
/// `len` indicates how many bytes we should try to write.
|
||||||
|
/// `dest` is where the return value should be stored: number of bytes written, or `-1` in case of error.
|
||||||
fn pwrite<'tcx>(
|
fn pwrite<'tcx>(
|
||||||
&self,
|
&self,
|
||||||
_communicate_allowed: bool,
|
_communicate_allowed: bool,
|
||||||
_bytes: &[u8],
|
_ptr: Pointer,
|
||||||
|
_len: usize,
|
||||||
_offset: u64,
|
_offset: u64,
|
||||||
|
_dest: &MPlaceTy<'tcx>,
|
||||||
_ecx: &mut MiriInterpCx<'tcx>,
|
_ecx: &mut MiriInterpCx<'tcx>,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
) -> InterpResult<'tcx> {
|
||||||
throw_unsup_format!("cannot pwrite to {}", self.name());
|
throw_unsup_format!("cannot pwrite to {}", self.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,14 +140,18 @@ impl FileDescription for io::Stdin {
|
|||||||
&self,
|
&self,
|
||||||
_self_ref: &FileDescriptionRef,
|
_self_ref: &FileDescriptionRef,
|
||||||
communicate_allowed: bool,
|
communicate_allowed: bool,
|
||||||
bytes: &mut [u8],
|
ptr: Pointer,
|
||||||
_ecx: &mut MiriInterpCx<'tcx>,
|
len: usize,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
dest: &MPlaceTy<'tcx>,
|
||||||
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
|
let mut bytes = vec![0; len];
|
||||||
if !communicate_allowed {
|
if !communicate_allowed {
|
||||||
// We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
|
// We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
|
||||||
helpers::isolation_abort_error("`read` from stdin")?;
|
helpers::isolation_abort_error("`read` from stdin")?;
|
||||||
}
|
}
|
||||||
Ok(Read::read(&mut { self }, bytes))
|
let result = Read::read(&mut { self }, &mut bytes);
|
||||||
|
ecx.return_read_bytes_and_count(ptr, &bytes, result, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_tty(&self, communicate_allowed: bool) -> bool {
|
fn is_tty(&self, communicate_allowed: bool) -> bool {
|
||||||
@ -149,9 +168,12 @@ impl FileDescription for io::Stdout {
|
|||||||
&self,
|
&self,
|
||||||
_self_ref: &FileDescriptionRef,
|
_self_ref: &FileDescriptionRef,
|
||||||
_communicate_allowed: bool,
|
_communicate_allowed: bool,
|
||||||
bytes: &[u8],
|
ptr: Pointer,
|
||||||
_ecx: &mut MiriInterpCx<'tcx>,
|
len: usize,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
dest: &MPlaceTy<'tcx>,
|
||||||
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
|
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
|
||||||
// We allow writing to stderr even with isolation enabled.
|
// We allow writing to stderr even with isolation enabled.
|
||||||
let result = Write::write(&mut { self }, bytes);
|
let result = Write::write(&mut { self }, bytes);
|
||||||
// Stdout is buffered, flush to make sure it appears on the
|
// Stdout is buffered, flush to make sure it appears on the
|
||||||
@ -160,8 +182,7 @@ impl FileDescription for io::Stdout {
|
|||||||
// the host -- there is no good in adding extra buffering
|
// the host -- there is no good in adding extra buffering
|
||||||
// here.
|
// here.
|
||||||
io::stdout().flush().unwrap();
|
io::stdout().flush().unwrap();
|
||||||
|
ecx.return_written_byte_count_or_error(result, dest)
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_tty(&self, communicate_allowed: bool) -> bool {
|
fn is_tty(&self, communicate_allowed: bool) -> bool {
|
||||||
@ -178,12 +199,16 @@ impl FileDescription for io::Stderr {
|
|||||||
&self,
|
&self,
|
||||||
_self_ref: &FileDescriptionRef,
|
_self_ref: &FileDescriptionRef,
|
||||||
_communicate_allowed: bool,
|
_communicate_allowed: bool,
|
||||||
bytes: &[u8],
|
ptr: Pointer,
|
||||||
_ecx: &mut MiriInterpCx<'tcx>,
|
len: usize,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
dest: &MPlaceTy<'tcx>,
|
||||||
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
|
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
|
||||||
// We allow writing to stderr even with isolation enabled.
|
// We allow writing to stderr even with isolation enabled.
|
||||||
// No need to flush, stderr is not buffered.
|
// No need to flush, stderr is not buffered.
|
||||||
Ok(Write::write(&mut { self }, bytes))
|
let result = Write::write(&mut { self }, bytes);
|
||||||
|
ecx.return_written_byte_count_or_error(result, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_tty(&self, communicate_allowed: bool) -> bool {
|
fn is_tty(&self, communicate_allowed: bool) -> bool {
|
||||||
@ -204,11 +229,14 @@ impl FileDescription for NullOutput {
|
|||||||
&self,
|
&self,
|
||||||
_self_ref: &FileDescriptionRef,
|
_self_ref: &FileDescriptionRef,
|
||||||
_communicate_allowed: bool,
|
_communicate_allowed: bool,
|
||||||
bytes: &[u8],
|
_ptr: Pointer,
|
||||||
_ecx: &mut MiriInterpCx<'tcx>,
|
len: usize,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
dest: &MPlaceTy<'tcx>,
|
||||||
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
// We just don't write anything, but report to the user that we did.
|
// We just don't write anything, but report to the user that we did.
|
||||||
Ok(Ok(bytes.len()))
|
let result = Ok(len);
|
||||||
|
ecx.return_written_byte_count_or_error(result, dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -535,7 +563,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
buf: Pointer,
|
buf: Pointer,
|
||||||
count: u64,
|
count: u64,
|
||||||
offset: Option<i128>,
|
offset: Option<i128>,
|
||||||
) -> InterpResult<'tcx, Scalar> {
|
dest: &MPlaceTy<'tcx>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
// Isolation check is done via `FileDescription` trait.
|
// Isolation check is done via `FileDescription` trait.
|
||||||
@ -550,48 +579,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let count = count
|
let count = count
|
||||||
.min(u64::try_from(this.target_isize_max()).unwrap())
|
.min(u64::try_from(this.target_isize_max()).unwrap())
|
||||||
.min(u64::try_from(isize::MAX).unwrap());
|
.min(u64::try_from(isize::MAX).unwrap());
|
||||||
|
let count = usize::try_from(count).unwrap(); // now it fits in a `usize`
|
||||||
let communicate = this.machine.communicate();
|
let communicate = this.machine.communicate();
|
||||||
|
|
||||||
// We temporarily dup the FD to be able to retain mutable access to `this`.
|
// We temporarily dup the FD to be able to retain mutable access to `this`.
|
||||||
let Some(fd) = this.machine.fds.get(fd_num) else {
|
let Some(fd) = this.machine.fds.get(fd_num) else {
|
||||||
trace!("read: FD not found");
|
trace!("read: FD not found");
|
||||||
return Ok(Scalar::from_target_isize(this.fd_not_found()?, this));
|
let res: i32 = this.fd_not_found()?;
|
||||||
|
this.write_int(res, dest)?;
|
||||||
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!("read: FD mapped to {fd:?}");
|
trace!("read: FD mapped to {fd:?}");
|
||||||
// We want to read at most `count` bytes. We are sure that `count` is not negative
|
// We want to read at most `count` bytes. We are sure that `count` is not negative
|
||||||
// because it was a target's `usize`. Also we are sure that its smaller than
|
// because it was a target's `usize`. Also we are sure that its smaller than
|
||||||
// `usize::MAX` because it is bounded by the host's `isize`.
|
// `usize::MAX` because it is bounded by the host's `isize`.
|
||||||
let mut bytes = vec![0; usize::try_from(count).unwrap()];
|
|
||||||
let result = match offset {
|
match offset {
|
||||||
None => fd.read(&fd, communicate, &mut bytes, this),
|
None => fd.read(&fd, communicate, buf, count, dest, this)?,
|
||||||
Some(offset) => {
|
Some(offset) => {
|
||||||
let Ok(offset) = u64::try_from(offset) else {
|
let Ok(offset) = u64::try_from(offset) else {
|
||||||
let einval = this.eval_libc("EINVAL");
|
let einval = this.eval_libc("EINVAL");
|
||||||
this.set_last_error(einval)?;
|
this.set_last_error(einval)?;
|
||||||
return Ok(Scalar::from_target_isize(-1, this));
|
this.write_int(-1, dest)?;
|
||||||
|
return Ok(());
|
||||||
};
|
};
|
||||||
fd.pread(communicate, &mut bytes, offset, this)
|
fd.pread(communicate, offset, buf, count, dest, this)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Ok(())
|
||||||
// `File::read` never returns a value larger than `count`, so this cannot fail.
|
|
||||||
match result?.map(|c| i64::try_from(c).unwrap()) {
|
|
||||||
Ok(read_bytes) => {
|
|
||||||
// If reading to `bytes` did not fail, we write those bytes to the buffer.
|
|
||||||
// Crucially, if fewer than `bytes.len()` bytes were read, only write
|
|
||||||
// that much into the output buffer!
|
|
||||||
this.write_bytes_ptr(
|
|
||||||
buf,
|
|
||||||
bytes[..usize::try_from(read_bytes).unwrap()].iter().copied(),
|
|
||||||
)?;
|
|
||||||
Ok(Scalar::from_target_isize(read_bytes, this))
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
this.set_last_error_from_io_error(e)?;
|
|
||||||
Ok(Scalar::from_target_isize(-1, this))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(
|
fn write(
|
||||||
@ -600,7 +616,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
buf: Pointer,
|
buf: Pointer,
|
||||||
count: u64,
|
count: u64,
|
||||||
offset: Option<i128>,
|
offset: Option<i128>,
|
||||||
) -> InterpResult<'tcx, Scalar> {
|
dest: &MPlaceTy<'tcx>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
// Isolation check is done via `FileDescription` trait.
|
// Isolation check is done via `FileDescription` trait.
|
||||||
@ -613,27 +630,72 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let count = count
|
let count = count
|
||||||
.min(u64::try_from(this.target_isize_max()).unwrap())
|
.min(u64::try_from(this.target_isize_max()).unwrap())
|
||||||
.min(u64::try_from(isize::MAX).unwrap());
|
.min(u64::try_from(isize::MAX).unwrap());
|
||||||
|
let count = usize::try_from(count).unwrap(); // now it fits in a `usize`
|
||||||
let communicate = this.machine.communicate();
|
let communicate = this.machine.communicate();
|
||||||
|
|
||||||
let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned();
|
|
||||||
// We temporarily dup the FD to be able to retain mutable access to `this`.
|
// We temporarily dup the FD to be able to retain mutable access to `this`.
|
||||||
let Some(fd) = this.machine.fds.get(fd_num) else {
|
let Some(fd) = this.machine.fds.get(fd_num) else {
|
||||||
return Ok(Scalar::from_target_isize(this.fd_not_found()?, this));
|
let res: i32 = this.fd_not_found()?;
|
||||||
|
this.write_int(res, dest)?;
|
||||||
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = match offset {
|
match offset {
|
||||||
None => fd.write(&fd, communicate, &bytes, this),
|
None => fd.write(&fd, communicate, buf, count, dest, this)?,
|
||||||
Some(offset) => {
|
Some(offset) => {
|
||||||
let Ok(offset) = u64::try_from(offset) else {
|
let Ok(offset) = u64::try_from(offset) else {
|
||||||
let einval = this.eval_libc("EINVAL");
|
let einval = this.eval_libc("EINVAL");
|
||||||
this.set_last_error(einval)?;
|
this.set_last_error(einval)?;
|
||||||
return Ok(Scalar::from_target_isize(-1, this));
|
this.write_int(-1, dest)?;
|
||||||
|
return Ok(());
|
||||||
};
|
};
|
||||||
fd.pwrite(communicate, &bytes, offset, this)
|
fd.pwrite(communicate, buf, count, offset, dest, this)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
let result = result?.map(|c| i64::try_from(c).unwrap());
|
/// Helper to implement `FileDescription::read`:
|
||||||
Ok(Scalar::from_target_isize(this.try_unwrap_io_result(result)?, this))
|
/// `result` should be the return value of some underlying `read` call that used `bytes` as its output buffer.
|
||||||
|
/// The length of `bytes` must not exceed either the host's or the target's `isize`.
|
||||||
|
/// If `Result` indicates success, `bytes` is written to `buf` and the size is written to `dest`.
|
||||||
|
/// Otherwise, `-1` is written to `dest` and the last libc error is set appropriately.
|
||||||
|
fn return_read_bytes_and_count(
|
||||||
|
&mut self,
|
||||||
|
buf: Pointer,
|
||||||
|
bytes: &[u8],
|
||||||
|
result: io::Result<usize>,
|
||||||
|
dest: &MPlaceTy<'tcx>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
|
let this = self.eval_context_mut();
|
||||||
|
match result {
|
||||||
|
Ok(read_bytes) => {
|
||||||
|
// If reading to `bytes` did not fail, we write those bytes to the buffer.
|
||||||
|
// Crucially, if fewer than `bytes.len()` bytes were read, only write
|
||||||
|
// that much into the output buffer!
|
||||||
|
this.write_bytes_ptr(buf, bytes[..read_bytes].iter().copied())?;
|
||||||
|
// The actual read size is always less than what got originally requested so this cannot fail.
|
||||||
|
this.write_int(u64::try_from(read_bytes).unwrap(), dest)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
this.set_last_error_from_io_error(e)?;
|
||||||
|
this.write_int(-1, dest)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function writes the number of written bytes (given in `result`) to `dest`, or sets the
|
||||||
|
/// last libc error and writes -1 to dest.
|
||||||
|
fn return_written_byte_count_or_error(
|
||||||
|
&mut self,
|
||||||
|
result: io::Result<usize>,
|
||||||
|
dest: &MPlaceTy<'tcx>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
|
let this = self.eval_context_mut();
|
||||||
|
let result = this.try_unwrap_io_result(result.map(|c| i64::try_from(c).unwrap()))?;
|
||||||
|
this.write_int(result, dest)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,8 +92,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let fd = this.read_scalar(fd)?.to_i32()?;
|
let fd = this.read_scalar(fd)?.to_i32()?;
|
||||||
let buf = this.read_pointer(buf)?;
|
let buf = this.read_pointer(buf)?;
|
||||||
let count = this.read_target_usize(count)?;
|
let count = this.read_target_usize(count)?;
|
||||||
let result = this.read(fd, buf, count, None)?;
|
this.read(fd, buf, count, None, dest)?;
|
||||||
this.write_scalar(result, dest)?;
|
|
||||||
}
|
}
|
||||||
"write" => {
|
"write" => {
|
||||||
let [fd, buf, n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
let [fd, buf, n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||||
@ -101,9 +100,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let buf = this.read_pointer(buf)?;
|
let buf = this.read_pointer(buf)?;
|
||||||
let count = this.read_target_usize(n)?;
|
let count = this.read_target_usize(n)?;
|
||||||
trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
|
trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
|
||||||
let result = this.write(fd, buf, count, None)?;
|
this.write(fd, buf, count, None, dest)?;
|
||||||
// Now, `result` is the value we return back to the program.
|
|
||||||
this.write_scalar(result, dest)?;
|
|
||||||
}
|
}
|
||||||
"pread" => {
|
"pread" => {
|
||||||
let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||||
@ -111,8 +108,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let buf = this.read_pointer(buf)?;
|
let buf = this.read_pointer(buf)?;
|
||||||
let count = this.read_target_usize(count)?;
|
let count = this.read_target_usize(count)?;
|
||||||
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
|
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
|
||||||
let result = this.read(fd, buf, count, Some(offset))?;
|
this.read(fd, buf, count, Some(offset), dest)?;
|
||||||
this.write_scalar(result, dest)?;
|
|
||||||
}
|
}
|
||||||
"pwrite" => {
|
"pwrite" => {
|
||||||
let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||||
@ -121,9 +117,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let count = this.read_target_usize(n)?;
|
let count = this.read_target_usize(n)?;
|
||||||
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
|
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
|
||||||
trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
|
trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
|
||||||
let result = this.write(fd, buf, count, Some(offset))?;
|
this.write(fd, buf, count, Some(offset), dest)?;
|
||||||
// Now, `result` is the value we return back to the program.
|
|
||||||
this.write_scalar(result, dest)?;
|
|
||||||
}
|
}
|
||||||
"pread64" => {
|
"pread64" => {
|
||||||
let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||||
@ -131,8 +125,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let buf = this.read_pointer(buf)?;
|
let buf = this.read_pointer(buf)?;
|
||||||
let count = this.read_target_usize(count)?;
|
let count = this.read_target_usize(count)?;
|
||||||
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
|
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
|
||||||
let result = this.read(fd, buf, count, Some(offset))?;
|
this.read(fd, buf, count, Some(offset), dest)?;
|
||||||
this.write_scalar(result, dest)?;
|
|
||||||
}
|
}
|
||||||
"pwrite64" => {
|
"pwrite64" => {
|
||||||
let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||||
@ -141,9 +134,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let count = this.read_target_usize(n)?;
|
let count = this.read_target_usize(n)?;
|
||||||
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
|
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
|
||||||
trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
|
trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
|
||||||
let result = this.write(fd, buf, count, Some(offset))?;
|
this.write(fd, buf, count, Some(offset), dest)?;
|
||||||
// Now, `result` is the value we return back to the program.
|
|
||||||
this.write_scalar(result, dest)?;
|
|
||||||
}
|
}
|
||||||
"close" => {
|
"close" => {
|
||||||
let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||||
|
@ -34,32 +34,43 @@ impl FileDescription for FileHandle {
|
|||||||
&self,
|
&self,
|
||||||
_self_ref: &FileDescriptionRef,
|
_self_ref: &FileDescriptionRef,
|
||||||
communicate_allowed: bool,
|
communicate_allowed: bool,
|
||||||
bytes: &mut [u8],
|
ptr: Pointer,
|
||||||
_ecx: &mut MiriInterpCx<'tcx>,
|
len: usize,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
dest: &MPlaceTy<'tcx>,
|
||||||
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
assert!(communicate_allowed, "isolation should have prevented even opening a file");
|
assert!(communicate_allowed, "isolation should have prevented even opening a file");
|
||||||
Ok((&mut &self.file).read(bytes))
|
let mut bytes = vec![0; len];
|
||||||
|
let result = (&mut &self.file).read(&mut bytes);
|
||||||
|
ecx.return_read_bytes_and_count(ptr, &bytes, result, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<'tcx>(
|
fn write<'tcx>(
|
||||||
&self,
|
&self,
|
||||||
_self_ref: &FileDescriptionRef,
|
_self_ref: &FileDescriptionRef,
|
||||||
communicate_allowed: bool,
|
communicate_allowed: bool,
|
||||||
bytes: &[u8],
|
ptr: Pointer,
|
||||||
_ecx: &mut MiriInterpCx<'tcx>,
|
len: usize,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
dest: &MPlaceTy<'tcx>,
|
||||||
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
assert!(communicate_allowed, "isolation should have prevented even opening a file");
|
assert!(communicate_allowed, "isolation should have prevented even opening a file");
|
||||||
Ok((&mut &self.file).write(bytes))
|
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
|
||||||
|
let result = (&mut &self.file).write(bytes);
|
||||||
|
ecx.return_written_byte_count_or_error(result, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pread<'tcx>(
|
fn pread<'tcx>(
|
||||||
&self,
|
&self,
|
||||||
communicate_allowed: bool,
|
communicate_allowed: bool,
|
||||||
bytes: &mut [u8],
|
|
||||||
offset: u64,
|
offset: u64,
|
||||||
_ecx: &mut MiriInterpCx<'tcx>,
|
ptr: Pointer,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
len: usize,
|
||||||
|
dest: &MPlaceTy<'tcx>,
|
||||||
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
assert!(communicate_allowed, "isolation should have prevented even opening a file");
|
assert!(communicate_allowed, "isolation should have prevented even opening a file");
|
||||||
|
let mut bytes = vec![0; len];
|
||||||
// Emulates pread using seek + read + seek to restore cursor position.
|
// Emulates pread using seek + read + seek to restore cursor position.
|
||||||
// Correctness of this emulation relies on sequential nature of Miri execution.
|
// Correctness of this emulation relies on sequential nature of Miri execution.
|
||||||
// The closure is used to emulate `try` block, since we "bubble" `io::Error` using `?`.
|
// The closure is used to emulate `try` block, since we "bubble" `io::Error` using `?`.
|
||||||
@ -67,27 +78,31 @@ impl FileDescription for FileHandle {
|
|||||||
let mut f = || {
|
let mut f = || {
|
||||||
let cursor_pos = file.stream_position()?;
|
let cursor_pos = file.stream_position()?;
|
||||||
file.seek(SeekFrom::Start(offset))?;
|
file.seek(SeekFrom::Start(offset))?;
|
||||||
let res = file.read(bytes);
|
let res = file.read(&mut bytes);
|
||||||
// Attempt to restore cursor position even if the read has failed
|
// Attempt to restore cursor position even if the read has failed
|
||||||
file.seek(SeekFrom::Start(cursor_pos))
|
file.seek(SeekFrom::Start(cursor_pos))
|
||||||
.expect("failed to restore file position, this shouldn't be possible");
|
.expect("failed to restore file position, this shouldn't be possible");
|
||||||
res
|
res
|
||||||
};
|
};
|
||||||
Ok(f())
|
let result = f();
|
||||||
|
ecx.return_read_bytes_and_count(ptr, &bytes, result, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pwrite<'tcx>(
|
fn pwrite<'tcx>(
|
||||||
&self,
|
&self,
|
||||||
communicate_allowed: bool,
|
communicate_allowed: bool,
|
||||||
bytes: &[u8],
|
ptr: Pointer,
|
||||||
|
len: usize,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
_ecx: &mut MiriInterpCx<'tcx>,
|
dest: &MPlaceTy<'tcx>,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
assert!(communicate_allowed, "isolation should have prevented even opening a file");
|
assert!(communicate_allowed, "isolation should have prevented even opening a file");
|
||||||
// Emulates pwrite using seek + write + seek to restore cursor position.
|
// Emulates pwrite using seek + write + seek to restore cursor position.
|
||||||
// Correctness of this emulation relies on sequential nature of Miri execution.
|
// Correctness of this emulation relies on sequential nature of Miri execution.
|
||||||
// The closure is used to emulate `try` block, since we "bubble" `io::Error` using `?`.
|
// The closure is used to emulate `try` block, since we "bubble" `io::Error` using `?`.
|
||||||
let file = &mut &self.file;
|
let file = &mut &self.file;
|
||||||
|
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
|
||||||
let mut f = || {
|
let mut f = || {
|
||||||
let cursor_pos = file.stream_position()?;
|
let cursor_pos = file.stream_position()?;
|
||||||
file.seek(SeekFrom::Start(offset))?;
|
file.seek(SeekFrom::Start(offset))?;
|
||||||
@ -97,7 +112,8 @@ impl FileDescription for FileHandle {
|
|||||||
.expect("failed to restore file position, this shouldn't be possible");
|
.expect("failed to restore file position, this shouldn't be possible");
|
||||||
res
|
res
|
||||||
};
|
};
|
||||||
Ok(f())
|
let result = f();
|
||||||
|
ecx.return_written_byte_count_or_error(result, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seek<'tcx>(
|
fn seek<'tcx>(
|
||||||
|
@ -2,18 +2,12 @@
|
|||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
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 crate::shims::unix::fd::FileDescriptionRef;
|
use crate::shims::unix::fd::FileDescriptionRef;
|
||||||
use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
|
use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
|
||||||
use crate::shims::unix::*;
|
use crate::shims::unix::*;
|
||||||
use crate::{concurrency::VClock, *};
|
use crate::{concurrency::VClock, *};
|
||||||
|
|
||||||
// We'll only do reads and writes in chunks of size u64.
|
|
||||||
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;
|
||||||
|
|
||||||
@ -62,37 +56,50 @@ impl FileDescription for Event {
|
|||||||
&self,
|
&self,
|
||||||
self_ref: &FileDescriptionRef,
|
self_ref: &FileDescriptionRef,
|
||||||
_communicate_allowed: bool,
|
_communicate_allowed: bool,
|
||||||
bytes: &mut [u8],
|
ptr: Pointer,
|
||||||
|
len: usize,
|
||||||
|
dest: &MPlaceTy<'tcx>,
|
||||||
ecx: &mut MiriInterpCx<'tcx>,
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
) -> InterpResult<'tcx> {
|
||||||
|
// We're treating the buffer as a `u64`.
|
||||||
|
let ty = ecx.machine.layouts.u64;
|
||||||
// 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_ARRAY_SIZE>() else {
|
if len < ty.size.bytes_usize() {
|
||||||
return Ok(Err(Error::from(ErrorKind::InvalidInput)));
|
ecx.set_last_error_from_io_error(Error::from(ErrorKind::InvalidInput))?;
|
||||||
};
|
ecx.write_int(-1, dest)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// eventfd read at the size of u64.
|
||||||
|
let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty);
|
||||||
|
|
||||||
// Block when counter == 0.
|
// Block when counter == 0.
|
||||||
let counter = self.counter.get();
|
let counter = self.counter.get();
|
||||||
if counter == 0 {
|
if counter == 0 {
|
||||||
if self.is_nonblock {
|
if self.is_nonblock {
|
||||||
return Ok(Err(Error::from(ErrorKind::WouldBlock)));
|
ecx.set_last_error_from_io_error(Error::from(ErrorKind::WouldBlock))?;
|
||||||
} else {
|
ecx.write_int(-1, dest)?;
|
||||||
//FIXME: blocking is not supported
|
return Ok(());
|
||||||
throw_unsup_format!("eventfd: blocking is unsupported");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw_unsup_format!("eventfd: blocking is unsupported");
|
||||||
} else {
|
} else {
|
||||||
// Synchronize with all prior `write` calls to this FD.
|
// Synchronize with all prior `write` calls to this FD.
|
||||||
ecx.acquire_clock(&self.clock.borrow());
|
ecx.acquire_clock(&self.clock.borrow());
|
||||||
// Return the counter in the host endianness using the buffer provided by caller.
|
|
||||||
*bytes = match ecx.tcx.sess.target.endian {
|
// Give old counter value to userspace, and set counter value to 0.
|
||||||
Endian::Little => counter.to_le_bytes(),
|
ecx.write_int(counter, &buf_place)?;
|
||||||
Endian::Big => counter.to_be_bytes(),
|
|
||||||
};
|
|
||||||
self.counter.set(0);
|
self.counter.set(0);
|
||||||
|
|
||||||
// When any of the event happened, we check and update the status of all supported event
|
// When any of the event happened, we check and update the status of all supported event
|
||||||
// types for current file description.
|
// types for current file description.
|
||||||
ecx.check_and_update_readiness(self_ref)?;
|
ecx.check_and_update_readiness(self_ref)?;
|
||||||
|
|
||||||
return Ok(Ok(U64_ARRAY_SIZE));
|
// Tell userspace how many bytes we wrote.
|
||||||
|
ecx.write_int(buf_place.layout.size.bytes(), dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A write call adds the 8-byte integer value supplied in
|
/// A write call adds the 8-byte integer value supplied in
|
||||||
@ -111,21 +118,27 @@ impl FileDescription for Event {
|
|||||||
&self,
|
&self,
|
||||||
self_ref: &FileDescriptionRef,
|
self_ref: &FileDescriptionRef,
|
||||||
_communicate_allowed: bool,
|
_communicate_allowed: bool,
|
||||||
bytes: &[u8],
|
ptr: Pointer,
|
||||||
|
len: usize,
|
||||||
|
dest: &MPlaceTy<'tcx>,
|
||||||
ecx: &mut MiriInterpCx<'tcx>,
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
) -> InterpResult<'tcx> {
|
||||||
|
// We're treating the buffer as a `u64`.
|
||||||
|
let ty = ecx.machine.layouts.u64;
|
||||||
// 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_ARRAY_SIZE>() else {
|
if len < ty.layout.size.bytes_usize() {
|
||||||
return Ok(Err(Error::from(ErrorKind::InvalidInput)));
|
let result = Err(Error::from(ErrorKind::InvalidInput));
|
||||||
};
|
return ecx.return_written_byte_count_or_error(result, dest);
|
||||||
// Convert from bytes to int according to host endianness.
|
}
|
||||||
let num = match ecx.tcx.sess.target.endian {
|
|
||||||
Endian::Little => u64::from_le_bytes(*bytes),
|
// Read the user supplied value from the pointer.
|
||||||
Endian::Big => u64::from_be_bytes(*bytes),
|
let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty);
|
||||||
};
|
let num = ecx.read_scalar(&buf_place)?.to_u64()?;
|
||||||
|
|
||||||
// u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1.
|
// u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1.
|
||||||
if num == u64::MAX {
|
if num == u64::MAX {
|
||||||
return Ok(Err(Error::from(ErrorKind::InvalidInput)));
|
let result = Err(Error::from(ErrorKind::InvalidInput));
|
||||||
|
return ecx.return_written_byte_count_or_error(result, dest);
|
||||||
}
|
}
|
||||||
// If the addition does not let the counter to exceed the maximum value, update the counter.
|
// If the addition does not let the counter to exceed the maximum value, update the counter.
|
||||||
// Else, block.
|
// Else, block.
|
||||||
@ -137,20 +150,20 @@ impl FileDescription for Event {
|
|||||||
}
|
}
|
||||||
self.counter.set(new_count);
|
self.counter.set(new_count);
|
||||||
}
|
}
|
||||||
None | Some(u64::MAX) => {
|
None | Some(u64::MAX) =>
|
||||||
if self.is_nonblock {
|
if self.is_nonblock {
|
||||||
return Ok(Err(Error::from(ErrorKind::WouldBlock)));
|
let result = Err(Error::from(ErrorKind::WouldBlock));
|
||||||
|
return ecx.return_written_byte_count_or_error(result, dest);
|
||||||
} else {
|
} else {
|
||||||
//FIXME: blocking is not supported
|
|
||||||
throw_unsup_format!("eventfd: blocking is unsupported");
|
throw_unsup_format!("eventfd: blocking is unsupported");
|
||||||
}
|
},
|
||||||
}
|
|
||||||
};
|
};
|
||||||
// When any of the event happened, we check and update the status of all supported event
|
// When any of the event happened, we check and update the status of all supported event
|
||||||
// types for current file description.
|
// types for current file description.
|
||||||
ecx.check_and_update_readiness(self_ref)?;
|
ecx.check_and_update_readiness(self_ref)?;
|
||||||
|
|
||||||
Ok(Ok(U64_ARRAY_SIZE))
|
// Return how many bytes we read.
|
||||||
|
ecx.write_int(buf_place.layout.size.bytes(), dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,8 @@ use std::collections::VecDeque;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{Error, ErrorKind, Read};
|
use std::io::{Error, ErrorKind, Read};
|
||||||
|
|
||||||
|
use rustc_target::abi::Size;
|
||||||
|
|
||||||
use crate::shims::unix::fd::{FileDescriptionRef, WeakFileDescriptionRef};
|
use crate::shims::unix::fd::{FileDescriptionRef, WeakFileDescriptionRef};
|
||||||
use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
|
use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
|
||||||
use crate::shims::unix::*;
|
use crate::shims::unix::*;
|
||||||
@ -126,14 +128,17 @@ impl FileDescription for AnonSocket {
|
|||||||
&self,
|
&self,
|
||||||
_self_ref: &FileDescriptionRef,
|
_self_ref: &FileDescriptionRef,
|
||||||
_communicate_allowed: bool,
|
_communicate_allowed: bool,
|
||||||
bytes: &mut [u8],
|
ptr: Pointer,
|
||||||
|
len: usize,
|
||||||
|
dest: &MPlaceTy<'tcx>,
|
||||||
ecx: &mut MiriInterpCx<'tcx>,
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
) -> InterpResult<'tcx> {
|
||||||
let request_byte_size = bytes.len();
|
let mut bytes = vec![0; len];
|
||||||
|
|
||||||
// Always succeed on read size 0.
|
// Always succeed on read size 0.
|
||||||
if request_byte_size == 0 {
|
if len == 0 {
|
||||||
return Ok(Ok(0));
|
let result = Ok(0);
|
||||||
|
return ecx.return_read_bytes_and_count(ptr, &bytes, result, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(readbuf) = &self.readbuf else {
|
let Some(readbuf) = &self.readbuf else {
|
||||||
@ -146,7 +151,8 @@ impl FileDescription for AnonSocket {
|
|||||||
if self.peer_fd().upgrade().is_none() {
|
if self.peer_fd().upgrade().is_none() {
|
||||||
// Socketpair with no peer and empty buffer.
|
// Socketpair with no peer and empty buffer.
|
||||||
// 0 bytes successfully read indicates end-of-file.
|
// 0 bytes successfully read indicates end-of-file.
|
||||||
return Ok(Ok(0));
|
let result = Ok(0);
|
||||||
|
return ecx.return_read_bytes_and_count(ptr, &bytes, result, dest);
|
||||||
} else {
|
} else {
|
||||||
if self.is_nonblock {
|
if self.is_nonblock {
|
||||||
// Non-blocking socketpair with writer and empty buffer.
|
// Non-blocking socketpair with writer and empty buffer.
|
||||||
@ -154,7 +160,8 @@ impl FileDescription for AnonSocket {
|
|||||||
// EAGAIN or EWOULDBLOCK can be returned for socket,
|
// EAGAIN or EWOULDBLOCK can be returned for socket,
|
||||||
// POSIX.1-2001 allows either error to be returned for this case.
|
// POSIX.1-2001 allows either error to be returned for this case.
|
||||||
// Since there is no ErrorKind for EAGAIN, WouldBlock is used.
|
// Since there is no ErrorKind for EAGAIN, WouldBlock is used.
|
||||||
return Ok(Err(Error::from(ErrorKind::WouldBlock)));
|
let result = Err(Error::from(ErrorKind::WouldBlock));
|
||||||
|
return ecx.return_read_bytes_and_count(ptr, &bytes, result, dest);
|
||||||
} else {
|
} else {
|
||||||
// Blocking socketpair with writer and empty buffer.
|
// Blocking socketpair with writer and empty buffer.
|
||||||
// FIXME: blocking is currently not supported
|
// FIXME: blocking is currently not supported
|
||||||
@ -170,7 +177,7 @@ impl FileDescription for AnonSocket {
|
|||||||
|
|
||||||
// Do full read / partial read based on the space available.
|
// Do full read / partial read based on the space available.
|
||||||
// Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
|
// Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
|
||||||
let actual_read_size = readbuf.buf.read(bytes).unwrap();
|
let actual_read_size = readbuf.buf.read(&mut bytes).unwrap();
|
||||||
|
|
||||||
// Need to drop before others can access the readbuf again.
|
// Need to drop before others can access the readbuf again.
|
||||||
drop(readbuf);
|
drop(readbuf);
|
||||||
@ -186,28 +193,32 @@ impl FileDescription for AnonSocket {
|
|||||||
ecx.check_and_update_readiness(&peer_fd)?;
|
ecx.check_and_update_readiness(&peer_fd)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Ok(actual_read_size));
|
let result = Ok(actual_read_size);
|
||||||
|
ecx.return_read_bytes_and_count(ptr, &bytes, result, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<'tcx>(
|
fn write<'tcx>(
|
||||||
&self,
|
&self,
|
||||||
_self_ref: &FileDescriptionRef,
|
_self_ref: &FileDescriptionRef,
|
||||||
_communicate_allowed: bool,
|
_communicate_allowed: bool,
|
||||||
bytes: &[u8],
|
ptr: Pointer,
|
||||||
|
len: usize,
|
||||||
|
dest: &MPlaceTy<'tcx>,
|
||||||
ecx: &mut MiriInterpCx<'tcx>,
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
) -> InterpResult<'tcx> {
|
||||||
let write_size = bytes.len();
|
|
||||||
// Always succeed on write size 0.
|
// Always succeed on write size 0.
|
||||||
// ("If count is zero and fd refers to a file other than a regular file, the results are not specified.")
|
// ("If count is zero and fd refers to a file other than a regular file, the results are not specified.")
|
||||||
if write_size == 0 {
|
if len == 0 {
|
||||||
return Ok(Ok(0));
|
let result = Ok(0);
|
||||||
|
return ecx.return_written_byte_count_or_error(result, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are writing to our peer's readbuf.
|
// We are writing to our peer's readbuf.
|
||||||
let Some(peer_fd) = self.peer_fd().upgrade() else {
|
let Some(peer_fd) = self.peer_fd().upgrade() else {
|
||||||
// If the upgrade from Weak to Rc fails, it indicates that all read ends have been
|
// If the upgrade from Weak to Rc fails, it indicates that all read ends have been
|
||||||
// closed.
|
// closed.
|
||||||
return Ok(Err(Error::from(ErrorKind::BrokenPipe)));
|
let result = Err(Error::from(ErrorKind::BrokenPipe));
|
||||||
|
return ecx.return_written_byte_count_or_error(result, dest);
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else {
|
let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else {
|
||||||
@ -221,7 +232,8 @@ impl FileDescription for AnonSocket {
|
|||||||
if available_space == 0 {
|
if available_space == 0 {
|
||||||
if self.is_nonblock {
|
if self.is_nonblock {
|
||||||
// Non-blocking socketpair with a full buffer.
|
// Non-blocking socketpair with a full buffer.
|
||||||
return Ok(Err(Error::from(ErrorKind::WouldBlock)));
|
let result = Err(Error::from(ErrorKind::WouldBlock));
|
||||||
|
return ecx.return_written_byte_count_or_error(result, dest);
|
||||||
} else {
|
} else {
|
||||||
// Blocking socketpair with a full buffer.
|
// Blocking socketpair with a full buffer.
|
||||||
throw_unsup_format!("socketpair write: blocking isn't supported yet");
|
throw_unsup_format!("socketpair write: blocking isn't supported yet");
|
||||||
@ -232,7 +244,8 @@ impl FileDescription for AnonSocket {
|
|||||||
writebuf.clock.join(clock);
|
writebuf.clock.join(clock);
|
||||||
}
|
}
|
||||||
// Do full write / partial write based on the space available.
|
// Do full write / partial write based on the space available.
|
||||||
let actual_write_size = write_size.min(available_space);
|
let actual_write_size = len.min(available_space);
|
||||||
|
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
|
||||||
writebuf.buf.extend(&bytes[..actual_write_size]);
|
writebuf.buf.extend(&bytes[..actual_write_size]);
|
||||||
|
|
||||||
// Need to stop accessing peer_fd so that it can be notified.
|
// Need to stop accessing peer_fd so that it can be notified.
|
||||||
@ -242,7 +255,8 @@ impl FileDescription for AnonSocket {
|
|||||||
// The kernel does this even if the fd was already readable before, so we follow suit.
|
// The kernel does this even if the fd was already readable before, so we follow suit.
|
||||||
ecx.check_and_update_readiness(&peer_fd)?;
|
ecx.check_and_update_readiness(&peer_fd)?;
|
||||||
|
|
||||||
return Ok(Ok(actual_write_size));
|
let result = Ok(actual_write_size);
|
||||||
|
ecx.return_written_byte_count_or_error(result, dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user