about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-09-22 14:54:30 +0000
committerbors <bors@rust-lang.org>2024-09-22 14:54:30 +0000
commit599b3295f53295b72ca1fab138a2786e667d5c06 (patch)
tree24557ec8797815c8b9d9f594543d09b1e9feb540
parent8243a8ca93133db93d2083e670777d7cc9265061 (diff)
parentd70fd882ef813190bf909de421358eec629bd088 (diff)
downloadrust-599b3295f53295b72ca1fab138a2786e667d5c06.tar.gz
rust-599b3295f53295b72ca1fab138a2786e667d5c06.zip
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.
-rw-r--r--src/tools/miri/src/shims/unix/fd.rs192
-rw-r--r--src/tools/miri/src/shims/unix/foreign_items.rs21
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs50
-rw-r--r--src/tools/miri/src/shims/unix/linux/eventfd.rs89
-rw-r--r--src/tools/miri/src/shims/unix/unnamed_socket.rs50
5 files changed, 249 insertions, 153 deletions
diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs
index 48bf959538b..6b78ce7ad47 100644
--- a/src/tools/miri/src/shims/unix/fd.rs
+++ b/src/tools/miri/src/shims/unix/fd.rs
@@ -25,49 +25,64 @@ pub(crate) enum FlockOp {
 pub trait FileDescription: std::fmt::Debug + Any {
     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>(
         &self,
         _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        _bytes: &mut [u8],
+        _ptr: Pointer,
+        _len: usize,
+        _dest: &MPlaceTy<'tcx>,
         _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<usize>> {
+    ) -> InterpResult<'tcx> {
         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>(
         &self,
         _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        _bytes: &[u8],
+        _ptr: Pointer,
+        _len: usize,
+        _dest: &MPlaceTy<'tcx>,
         _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<usize>> {
+    ) -> InterpResult<'tcx> {
         throw_unsup_format!("cannot write to {}", self.name());
     }
 
-    /// Reads as much as possible into the given buffer from a given offset,
-    /// and returns the number of bytes read.
+    /// Reads as much as possible into the given buffer `ptr` from a given offset.
+    /// `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>(
         &self,
         _communicate_allowed: bool,
-        _bytes: &mut [u8],
         _offset: u64,
+        _ptr: Pointer,
+        _len: usize,
+        _dest: &MPlaceTy<'tcx>,
         _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<usize>> {
+    ) -> InterpResult<'tcx> {
         throw_unsup_format!("cannot pread from {}", self.name());
     }
 
-    /// Writes as much as possible from the given buffer starting at a given offset,
-    /// and returns the number of bytes written.
+    /// Writes as much as possible from the given buffer `ptr` starting at a given offset.
+    /// `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>(
         &self,
         _communicate_allowed: bool,
-        _bytes: &[u8],
+        _ptr: Pointer,
+        _len: usize,
         _offset: u64,
+        _dest: &MPlaceTy<'tcx>,
         _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<usize>> {
+    ) -> InterpResult<'tcx> {
         throw_unsup_format!("cannot pwrite to {}", self.name());
     }
 
@@ -125,14 +140,18 @@ impl FileDescription for io::Stdin {
         &self,
         _self_ref: &FileDescriptionRef,
         communicate_allowed: bool,
-        bytes: &mut [u8],
-        _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<usize>> {
+        ptr: Pointer,
+        len: usize,
+        dest: &MPlaceTy<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx> {
+        let mut bytes = vec![0; len];
         if !communicate_allowed {
             // We want isolation mode to be deterministic, so we have to disallow all reads, even 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 {
@@ -149,9 +168,12 @@ impl FileDescription for io::Stdout {
         &self,
         _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        bytes: &[u8],
-        _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<usize>> {
+        ptr: Pointer,
+        len: 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.
         let result = Write::write(&mut { self }, bytes);
         // 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
         // here.
         io::stdout().flush().unwrap();
-
-        Ok(result)
+        ecx.return_written_byte_count_or_error(result, dest)
     }
 
     fn is_tty(&self, communicate_allowed: bool) -> bool {
@@ -178,12 +199,16 @@ impl FileDescription for io::Stderr {
         &self,
         _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        bytes: &[u8],
-        _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<usize>> {
+        ptr: Pointer,
+        len: 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.
         // 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 {
@@ -204,11 +229,14 @@ impl FileDescription for NullOutput {
         &self,
         _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        bytes: &[u8],
-        _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<usize>> {
+        _ptr: Pointer,
+        len: usize,
+        dest: &MPlaceTy<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx> {
         // 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,
         count: u64,
         offset: Option<i128>,
-    ) -> InterpResult<'tcx, Scalar> {
+        dest: &MPlaceTy<'tcx>,
+    ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
         // Isolation check is done via `FileDescription` trait.
@@ -550,48 +579,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let count = count
             .min(u64::try_from(this.target_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();
 
         // We temporarily dup the FD to be able to retain mutable access to `this`.
         let Some(fd) = this.machine.fds.get(fd_num) else {
             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:?}");
         // 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
         // `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 {
-            None => fd.read(&fd, communicate, &mut bytes, this),
+
+        match offset {
+            None => fd.read(&fd, communicate, buf, count, dest, this)?,
             Some(offset) => {
                 let Ok(offset) = u64::try_from(offset) else {
                     let einval = this.eval_libc("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)?
             }
         };
-
-        // `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))
-            }
-        }
+        Ok(())
     }
 
     fn write(
@@ -600,7 +616,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         buf: Pointer,
         count: u64,
         offset: Option<i128>,
-    ) -> InterpResult<'tcx, Scalar> {
+        dest: &MPlaceTy<'tcx>,
+    ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
         // Isolation check is done via `FileDescription` trait.
@@ -613,27 +630,72 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let count = count
             .min(u64::try_from(this.target_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 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`.
         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 {
-            None => fd.write(&fd, communicate, &bytes, this),
+        match offset {
+            None => fd.write(&fd, communicate, buf, count, dest, this)?,
             Some(offset) => {
                 let Ok(offset) = u64::try_from(offset) else {
                     let einval = this.eval_libc("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());
-        Ok(Scalar::from_target_isize(this.try_unwrap_io_result(result)?, this))
+    /// Helper to implement `FileDescription::read`:
+    /// `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(())
     }
 }
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index 73f933264fd..57dae050250 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -92,8 +92,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let fd = this.read_scalar(fd)?.to_i32()?;
                 let buf = this.read_pointer(buf)?;
                 let count = this.read_target_usize(count)?;
-                let result = this.read(fd, buf, count, None)?;
-                this.write_scalar(result, dest)?;
+                this.read(fd, buf, count, None, dest)?;
             }
             "write" => {
                 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 count = this.read_target_usize(n)?;
                 trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
-                let result = this.write(fd, buf, count, None)?;
-                // Now, `result` is the value we return back to the program.
-                this.write_scalar(result, dest)?;
+                this.write(fd, buf, count, None, dest)?;
             }
             "pread" => {
                 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 count = this.read_target_usize(count)?;
                 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.write_scalar(result, dest)?;
+                this.read(fd, buf, count, Some(offset), dest)?;
             }
             "pwrite" => {
                 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 offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
                 trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
-                let result = this.write(fd, buf, count, Some(offset))?;
-                // Now, `result` is the value we return back to the program.
-                this.write_scalar(result, dest)?;
+                this.write(fd, buf, count, Some(offset), dest)?;
             }
             "pread64" => {
                 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 count = this.read_target_usize(count)?;
                 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.write_scalar(result, dest)?;
+                this.read(fd, buf, count, Some(offset), dest)?;
             }
             "pwrite64" => {
                 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 offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
                 trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
-                let result = this.write(fd, buf, count, Some(offset))?;
-                // Now, `result` is the value we return back to the program.
-                this.write_scalar(result, dest)?;
+                this.write(fd, buf, count, Some(offset), dest)?;
             }
             "close" => {
                 let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index f5fe2b4b30b..1f7e1b3bd7c 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -34,32 +34,43 @@ impl FileDescription for FileHandle {
         &self,
         _self_ref: &FileDescriptionRef,
         communicate_allowed: bool,
-        bytes: &mut [u8],
-        _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<usize>> {
+        ptr: Pointer,
+        len: usize,
+        dest: &MPlaceTy<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx> {
         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>(
         &self,
         _self_ref: &FileDescriptionRef,
         communicate_allowed: bool,
-        bytes: &[u8],
-        _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<usize>> {
+        ptr: Pointer,
+        len: usize,
+        dest: &MPlaceTy<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx> {
         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>(
         &self,
         communicate_allowed: bool,
-        bytes: &mut [u8],
         offset: u64,
-        _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<usize>> {
+        ptr: Pointer,
+        len: usize,
+        dest: &MPlaceTy<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx> {
         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.
         // 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 `?`.
@@ -67,27 +78,31 @@ impl FileDescription for FileHandle {
         let mut f = || {
             let cursor_pos = file.stream_position()?;
             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
             file.seek(SeekFrom::Start(cursor_pos))
                 .expect("failed to restore file position, this shouldn't be possible");
             res
         };
-        Ok(f())
+        let result = f();
+        ecx.return_read_bytes_and_count(ptr, &bytes, result, dest)
     }
 
     fn pwrite<'tcx>(
         &self,
         communicate_allowed: bool,
-        bytes: &[u8],
+        ptr: Pointer,
+        len: usize,
         offset: u64,
-        _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> 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");
         // Emulates pwrite using seek + write + seek to restore cursor position.
         // 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 `?`.
         let file = &mut &self.file;
+        let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
         let mut f = || {
             let cursor_pos = file.stream_position()?;
             file.seek(SeekFrom::Start(offset))?;
@@ -97,7 +112,8 @@ impl FileDescription for FileHandle {
                 .expect("failed to restore file position, this shouldn't be possible");
             res
         };
-        Ok(f())
+        let result = f();
+        ecx.return_written_byte_count_or_error(result, dest)
     }
 
     fn seek<'tcx>(
diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs
index 77d16a032aa..d1d461daa99 100644
--- a/src/tools/miri/src/shims/unix/linux/eventfd.rs
+++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs
@@ -2,18 +2,12 @@
 use std::cell::{Cell, RefCell};
 use std::io;
 use std::io::{Error, ErrorKind};
-use std::mem;
-
-use rustc_target::abi::Endian;
 
 use crate::shims::unix::fd::FileDescriptionRef;
 use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
 use crate::shims::unix::*;
 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.
 const MAX_COUNTER: u64 = u64::MAX - 1;
 
@@ -62,37 +56,50 @@ impl FileDescription for Event {
         &self,
         self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        bytes: &mut [u8],
+        ptr: Pointer,
+        len: usize,
+        dest: &MPlaceTy<'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.
-        let Some(bytes) = bytes.first_chunk_mut::<U64_ARRAY_SIZE>() else {
-            return Ok(Err(Error::from(ErrorKind::InvalidInput)));
-        };
+        if len < ty.size.bytes_usize() {
+            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.
         let counter = self.counter.get();
         if counter == 0 {
             if self.is_nonblock {
-                return Ok(Err(Error::from(ErrorKind::WouldBlock)));
-            } else {
-                //FIXME: blocking is not supported
-                throw_unsup_format!("eventfd: blocking is unsupported");
+                ecx.set_last_error_from_io_error(Error::from(ErrorKind::WouldBlock))?;
+                ecx.write_int(-1, dest)?;
+                return Ok(());
             }
+
+            throw_unsup_format!("eventfd: blocking is unsupported");
         } else {
             // Synchronize with all prior `write` calls to this FD.
             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 {
-                Endian::Little => counter.to_le_bytes(),
-                Endian::Big => counter.to_be_bytes(),
-            };
+
+            // Give old counter value to userspace, and set counter value to 0.
+            ecx.write_int(counter, &buf_place)?;
             self.counter.set(0);
+
             // When any of the event happened, we check and update the status of all supported event
             // types for current file description.
             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
@@ -111,21 +118,27 @@ impl FileDescription for Event {
         &self,
         self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        bytes: &[u8],
+        ptr: Pointer,
+        len: usize,
+        dest: &MPlaceTy<'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.
-        let Some(bytes) = bytes.first_chunk::<U64_ARRAY_SIZE>() else {
-            return Ok(Err(Error::from(ErrorKind::InvalidInput)));
-        };
-        // Convert from bytes to int according to host endianness.
-        let num = match ecx.tcx.sess.target.endian {
-            Endian::Little => u64::from_le_bytes(*bytes),
-            Endian::Big => u64::from_be_bytes(*bytes),
-        };
+        if len < ty.layout.size.bytes_usize() {
+            let result = Err(Error::from(ErrorKind::InvalidInput));
+            return ecx.return_written_byte_count_or_error(result, dest);
+        }
+
+        // Read the user supplied value from the pointer.
+        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.
         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.
         // Else, block.
@@ -137,20 +150,20 @@ impl FileDescription for Event {
                 }
                 self.counter.set(new_count);
             }
-            None | Some(u64::MAX) => {
+            None | Some(u64::MAX) =>
                 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 {
-                    //FIXME: blocking is not supported
                     throw_unsup_format!("eventfd: blocking is unsupported");
-                }
-            }
+                },
         };
         // When any of the event happened, we check and update the status of all supported event
         // types for current file description.
         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)
     }
 }
 
diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs
index 127aef29244..db6872319ea 100644
--- a/src/tools/miri/src/shims/unix/unnamed_socket.rs
+++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs
@@ -7,6 +7,8 @@ use std::collections::VecDeque;
 use std::io;
 use std::io::{Error, ErrorKind, Read};
 
+use rustc_target::abi::Size;
+
 use crate::shims::unix::fd::{FileDescriptionRef, WeakFileDescriptionRef};
 use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
 use crate::shims::unix::*;
@@ -126,14 +128,17 @@ impl FileDescription for AnonSocket {
         &self,
         _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        bytes: &mut [u8],
+        ptr: Pointer,
+        len: usize,
+        dest: &MPlaceTy<'tcx>,
         ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<usize>> {
-        let request_byte_size = bytes.len();
+    ) -> InterpResult<'tcx> {
+        let mut bytes = vec![0; len];
 
         // Always succeed on read size 0.
-        if request_byte_size == 0 {
-            return Ok(Ok(0));
+        if len == 0 {
+            let result = Ok(0);
+            return ecx.return_read_bytes_and_count(ptr, &bytes, result, dest);
         }
 
         let Some(readbuf) = &self.readbuf else {
@@ -146,7 +151,8 @@ impl FileDescription for AnonSocket {
             if self.peer_fd().upgrade().is_none() {
                 // Socketpair with no peer and empty buffer.
                 // 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 {
                 if self.is_nonblock {
                     // Non-blocking socketpair with writer and empty buffer.
@@ -154,7 +160,8 @@ impl FileDescription for AnonSocket {
                     // EAGAIN or EWOULDBLOCK can be returned for socket,
                     // POSIX.1-2001 allows either error to be returned for this case.
                     // 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 {
                     // Blocking socketpair with writer and empty buffer.
                     // FIXME: blocking is currently not supported
@@ -170,7 +177,7 @@ impl FileDescription for AnonSocket {
 
         // Do full read / partial read based on the space available.
         // 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.
         drop(readbuf);
@@ -186,28 +193,32 @@ impl FileDescription for AnonSocket {
             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>(
         &self,
         _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        bytes: &[u8],
+        ptr: Pointer,
+        len: usize,
+        dest: &MPlaceTy<'tcx>,
         ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<usize>> {
-        let write_size = bytes.len();
+    ) -> InterpResult<'tcx> {
         // 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 write_size == 0 {
-            return Ok(Ok(0));
+        if len == 0 {
+            let result = Ok(0);
+            return ecx.return_written_byte_count_or_error(result, dest);
         }
 
         // We are writing to our peer's readbuf.
         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
             // 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 {
@@ -221,7 +232,8 @@ impl FileDescription for AnonSocket {
         if available_space == 0 {
             if self.is_nonblock {
                 // 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 {
                 // Blocking socketpair with a full buffer.
                 throw_unsup_format!("socketpair write: blocking isn't supported yet");
@@ -232,7 +244,8 @@ impl FileDescription for AnonSocket {
             writebuf.clock.join(clock);
         }
         // 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]);
 
         // 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.
         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)
     }
 }