diff --git a/src/helpers.rs b/src/helpers.rs index ba094c988e..322cea2b93 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,9 +1,9 @@ -use std::cmp; use std::collections::BTreeSet; use std::iter; use std::num::NonZero; use std::sync::Mutex; use std::time::Duration; +use std::{cmp, io}; use rand::RngCore; @@ -857,19 +857,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// /// This function uses `T: From` instead of `i32` directly because some IO related /// functions return different integer types (like `read`, that returns an `i64`). - fn try_unwrap_io_result>( + fn try_unwrap_io_result( &mut self, result: std::io::Result, - ) -> InterpResult<'tcx, T> { + ) -> InterpResult<'tcx, Scalar> { match result { - Ok(ok) => Ok(ok), + Ok(ok) => Ok(ok.into_scalar()), Err(e) => { - self.eval_context_mut().set_last_error_from_io_error(e)?; - Ok((-1).into()) + self.set_last_error_from_io_error(e)?; + Ok(T::neg_one()) } } } + /// Write an io result to a place. + /// No error means writing via `WriteIoResult` + /// Otherwise write `-1` and set the last error. + #[inline(always)] + fn write_io_result( + &mut self, + val: io::Result, + dest: &impl Writeable<'tcx, Provenance>, + ) -> InterpResult<'tcx> { + let result = self.try_unwrap_io_result(val)?; + self.eval_context_mut().write_scalar(result, dest) + } + /// Dereference a pointer operand to a place using `layout` instead of the pointer's declared type fn deref_pointer_as( &self, @@ -1434,3 +1447,35 @@ pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 { u32::try_from(len).unwrap() } } + +pub trait WriteIoResult { + fn into_scalar(self) -> Scalar; + fn neg_one() -> Scalar; +} + +impl WriteIoResult for () { + fn into_scalar(self) -> Scalar { + Scalar::from_i32(0) + } + fn neg_one() -> Scalar { + Scalar::from_i32(-1) + } +} + +impl WriteIoResult for i32 { + fn into_scalar(self) -> Scalar { + Scalar::from_i32(self) + } + fn neg_one() -> Scalar { + Scalar::from_i32(-1) + } +} + +impl WriteIoResult for i64 { + fn into_scalar(self) -> Scalar { + Scalar::from_i64(self) + } + fn neg_one() -> Scalar { + Scalar::from_i64(-1) + } +} diff --git a/src/shims/unix/env.rs b/src/shims/unix/env.rs index ac9933ea70..86d9d06d40 100644 --- a/src/shims/unix/env.rs +++ b/src/shims/unix/env.rs @@ -1,7 +1,7 @@ -use std::env; use std::ffi::{OsStr, OsString}; use std::io::ErrorKind; use std::mem; +use std::{env, io}; use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::layout::LayoutOf; @@ -236,7 +236,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Pointer::null()) } - fn chdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { + fn chdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, io::Result<()>> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("chdir"); @@ -244,13 +244,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`chdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - - return Ok(Scalar::from_i32(-1)); + return Ok(Err(ErrorKind::PermissionDenied.into())); } - let result = env::set_current_dir(path).map(|()| 0); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + Ok(env::set_current_dir(path)) } /// Updates the `environ` static. diff --git a/src/shims/unix/fd.rs b/src/shims/unix/fd.rs index 74e690a5f1..19ebc7b1d2 100644 --- a/src/shims/unix/fd.rs +++ b/src/shims/unix/fd.rs @@ -366,8 +366,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = file_descriptor.flock(this.machine.communicate(), parsed_op)?; drop(file_descriptor); // return `0` if flock is successful - let result = result.map(|()| 0i32); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + this.try_unwrap_io_result(result) } fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { @@ -438,7 +437,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = file_description.close(this.machine.communicate())?; // return `0` if close is successful let result = result.map(|()| 0i32); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + this.try_unwrap_io_result(result) } /// Function used when a file descriptor does not exist. It returns `Ok(-1)`and sets @@ -564,7 +563,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { drop(fd); let result = result?.map(|c| i64::try_from(c).unwrap()); - Ok(Scalar::from_target_isize(this.try_unwrap_io_result(result)?, this)) + match result { + Ok(written_bytes) => Ok(Scalar::from_target_isize(written_bytes, this)), + Err(e) => { + this.set_last_error_from_io_error(e)?; + Ok(Scalar::from_target_isize(-1, this)) + } + } } } diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 57930f9807..b7ad39dc93 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -78,7 +78,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "chdir" => { let [path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.chdir(path)?; - this.write_scalar(result, dest)?; + this.write_io_result(result, dest)?; } "getpid" => { let [] = this.check_shim(abi, Abi::C { unwind: false}, link_name, args)?; @@ -188,12 +188,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "unlink" => { let [path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.unlink(path)?; - this.write_scalar(result, dest)?; + this.write_io_result(result, dest)?; } "symlink" => { let [target, linkpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.symlink(target, linkpath)?; - this.write_scalar(result, dest)?; + this.write_io_result(result, dest)?; } "rename" => { let [oldpath, newpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -203,12 +203,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "mkdir" => { let [path, mode] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.mkdir(path, mode)?; - this.write_scalar(result, dest)?; + this.write_io_result(result, dest)?; } "rmdir" => { let [path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.rmdir(path)?; - this.write_scalar(result, dest)?; + this.write_io_result(result, dest)?; } "opendir" => { let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index 7891b65a9d..77d2e562fc 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -547,7 +547,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .open(path) .map(|file| this.machine.fds.insert_fd(FileHandle { file, writable })); - Ok(Scalar::from_i32(this.try_unwrap_io_result(fd)?)) + this.try_unwrap_io_result(fd) } fn lseek64(&mut self, fd: i32, offset: i128, whence: i32) -> InterpResult<'tcx, Scalar> { @@ -584,11 +584,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .map(|offset| i64::try_from(offset).unwrap()); drop(file_description); - let result = this.try_unwrap_io_result(result)?; - Ok(Scalar::from_i64(result)) + this.try_unwrap_io_result(result) } - fn unlink(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { + fn unlink(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, io::Result<()>> { let this = self.eval_context_mut(); let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; @@ -596,19 +595,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`unlink`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return Ok(Err(ErrorKind::PermissionDenied.into())); } - let result = remove_file(path).map(|_| 0); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + Ok(remove_file(path)) } fn symlink( &mut self, target_op: &OpTy<'tcx>, linkpath_op: &OpTy<'tcx>, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, io::Result<()>> { #[cfg(unix)] fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> { std::os::unix::fs::symlink(src, dst) @@ -627,12 +624,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`symlink`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return Ok(Err(ErrorKind::PermissionDenied.into())); } - let result = create_link(&target, &linkpath).map(|_| 0); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + Ok(create_link(&target, &linkpath)) } fn macos_fbsd_stat( @@ -934,10 +929,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = rename(oldpath, newpath).map(|_| 0); - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + this.try_unwrap_io_result(result) } - fn mkdir(&mut self, path_op: &OpTy<'tcx>, mode_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { + fn mkdir( + &mut self, + path_op: &OpTy<'tcx>, + mode_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, io::Result<()>> { let this = self.eval_context_mut(); #[cfg_attr(not(unix), allow(unused_variables))] @@ -952,8 +951,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`mkdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return Ok(Err(ErrorKind::PermissionDenied.into())); } #[cfg_attr(not(unix), allow(unused_mut))] @@ -967,12 +965,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { builder.mode(mode); } - let result = builder.create(path).map(|_| 0i32); - - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + Ok(builder.create(path)) } - fn rmdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { + fn rmdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, io::Result<()>> { let this = self.eval_context_mut(); let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; @@ -980,13 +976,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rmdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(Scalar::from_i32(-1)); + return Ok(Err(ErrorKind::PermissionDenied.into())); } - let result = remove_dir(path).map(|_| 0i32); - - Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) + Ok(remove_dir(path)) } fn opendir(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -1280,8 +1273,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let Ok(length) = length.try_into() { let result = file.set_len(length); drop(file_description); - let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; - Ok(Scalar::from_i32(result)) + this.try_unwrap_io_result(result) } else { drop(file_description); let einval = this.eval_libc("EINVAL"); @@ -1329,7 +1321,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { })?; let io_result = maybe_sync_file(file, *writable, File::sync_all); drop(file_description); - Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) + this.try_unwrap_io_result(io_result) } fn fdatasync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -1354,7 +1346,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { })?; let io_result = maybe_sync_file(file, *writable, File::sync_data); drop(file_description); - Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) + this.try_unwrap_io_result(io_result) } fn sync_file_range( @@ -1404,7 +1396,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { })?; let io_result = maybe_sync_file(file, *writable, File::sync_data); drop(file_description); - Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) + this.try_unwrap_io_result(io_result) } fn readlink(