From 88c74529c1210bdf06e30b70aa51721218512090 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 1 Oct 2024 08:41:54 +0200 Subject: [PATCH 1/3] move io error handling helpers to their own file --- src/tools/miri/src/helpers.rs | 185 +------------------------- src/tools/miri/src/lib.rs | 1 + src/tools/miri/src/shims/io_error.rs | 190 +++++++++++++++++++++++++++ src/tools/miri/src/shims/mod.rs | 1 + 4 files changed, 193 insertions(+), 184 deletions(-) create mode 100644 src/tools/miri/src/shims/io_error.rs diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 5324e4bec5b22..da0581ea86254 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -2,7 +2,7 @@ use std::collections::BTreeSet; use std::num::NonZero; use std::sync::Mutex; use std::time::Duration; -use std::{cmp, io, iter}; +use std::{cmp, iter}; use rand::RngCore; use rustc_apfloat::Float; @@ -31,65 +31,6 @@ pub enum AccessKind { Write, } -// This mapping should match `decode_error_kind` in -// . -const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { - use std::io::ErrorKind::*; - &[ - ("E2BIG", ArgumentListTooLong), - ("EADDRINUSE", AddrInUse), - ("EADDRNOTAVAIL", AddrNotAvailable), - ("EBUSY", ResourceBusy), - ("ECONNABORTED", ConnectionAborted), - ("ECONNREFUSED", ConnectionRefused), - ("ECONNRESET", ConnectionReset), - ("EDEADLK", Deadlock), - ("EDQUOT", FilesystemQuotaExceeded), - ("EEXIST", AlreadyExists), - ("EFBIG", FileTooLarge), - ("EHOSTUNREACH", HostUnreachable), - ("EINTR", Interrupted), - ("EINVAL", InvalidInput), - ("EISDIR", IsADirectory), - ("ELOOP", FilesystemLoop), - ("ENOENT", NotFound), - ("ENOMEM", OutOfMemory), - ("ENOSPC", StorageFull), - ("ENOSYS", Unsupported), - ("EMLINK", TooManyLinks), - ("ENAMETOOLONG", InvalidFilename), - ("ENETDOWN", NetworkDown), - ("ENETUNREACH", NetworkUnreachable), - ("ENOTCONN", NotConnected), - ("ENOTDIR", NotADirectory), - ("ENOTEMPTY", DirectoryNotEmpty), - ("EPIPE", BrokenPipe), - ("EROFS", ReadOnlyFilesystem), - ("ESPIPE", NotSeekable), - ("ESTALE", StaleNetworkFileHandle), - ("ETIMEDOUT", TimedOut), - ("ETXTBSY", ExecutableFileBusy), - ("EXDEV", CrossesDevices), - // The following have two valid options. We have both for the forwards mapping; only the - // first one will be used for the backwards mapping. - ("EPERM", PermissionDenied), - ("EACCES", PermissionDenied), - ("EWOULDBLOCK", WouldBlock), - ("EAGAIN", WouldBlock), - ] -}; -// This mapping should match `decode_error_kind` in -// . -const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { - use std::io::ErrorKind::*; - // FIXME: this is still incomplete. - &[ - ("ERROR_ACCESS_DENIED", PermissionDenied), - ("ERROR_FILE_NOT_FOUND", NotFound), - ("ERROR_INVALID_PARAMETER", InvalidInput), - ] -}; - /// Gets an instance for a path. /// /// A `None` namespace indicates we are looking for a module. @@ -745,130 +686,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { self.eval_context_ref().tcx.sess.target.families.iter().any(|f| f == "unix") } - /// Get last error variable as a place, lazily allocating thread-local storage for it if - /// necessary. - fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx>> { - let this = self.eval_context_mut(); - if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() { - Ok(errno_place.clone()) - } else { - // Allocate new place, set initial value to 0. - let errno_layout = this.machine.layouts.u32; - let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into())?; - this.write_scalar(Scalar::from_u32(0), &errno_place)?; - this.active_thread_mut().last_error = Some(errno_place.clone()); - Ok(errno_place) - } - } - - /// Sets the last error variable. - fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - let errno_place = this.last_error_place()?; - this.write_scalar(scalar, &errno_place) - } - - /// Gets the last error variable. - fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_mut(); - let errno_place = this.last_error_place()?; - this.read_scalar(&errno_place) - } - - /// This function tries to produce the most similar OS error from the `std::io::ErrorKind` - /// as a platform-specific errnum. - fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_ref(); - let target = &this.tcx.sess.target; - if target.families.iter().any(|f| f == "unix") { - for &(name, kind) in UNIX_IO_ERROR_TABLE { - if err.kind() == kind { - return Ok(this.eval_libc(name)); - } - } - throw_unsup_format!("unsupported io error: {err}") - } else if target.families.iter().any(|f| f == "windows") { - for &(name, kind) in WINDOWS_IO_ERROR_TABLE { - if err.kind() == kind { - return Ok(this.eval_windows("c", name)); - } - } - throw_unsup_format!("unsupported io error: {err}"); - } else { - throw_unsup_format!( - "converting io::Error into errnum is unsupported for OS {}", - target.os - ) - } - } - - /// The inverse of `io_error_to_errnum`. - #[allow(clippy::needless_return)] - fn try_errnum_to_io_error( - &self, - errnum: Scalar, - ) -> InterpResult<'tcx, Option> { - let this = self.eval_context_ref(); - let target = &this.tcx.sess.target; - if target.families.iter().any(|f| f == "unix") { - let errnum = errnum.to_i32()?; - for &(name, kind) in UNIX_IO_ERROR_TABLE { - if errnum == this.eval_libc_i32(name) { - return Ok(Some(kind)); - } - } - return Ok(None); - } else if target.families.iter().any(|f| f == "windows") { - let errnum = errnum.to_u32()?; - for &(name, kind) in WINDOWS_IO_ERROR_TABLE { - if errnum == this.eval_windows("c", name).to_u32()? { - return Ok(Some(kind)); - } - } - return Ok(None); - } else { - throw_unsup_format!( - "converting errnum into io::Error is unsupported for OS {}", - target.os - ) - } - } - - /// Sets the last OS error using a `std::io::ErrorKind`. - fn set_last_error_from_io_error(&mut self, err: std::io::Error) -> InterpResult<'tcx> { - self.set_last_error(self.io_error_to_errnum(err)?) - } - - /// Sets the last OS error using a `std::io::ErrorKind` and writes -1 to dest place. - fn set_last_error_and_return( - &mut self, - err: impl Into, - dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx> { - self.set_last_error(self.io_error_to_errnum(err.into())?)?; - self.write_int(-1, dest)?; - Ok(()) - } - - /// Helper function that consumes an `std::io::Result` and returns an - /// `InterpResult<'tcx,T>::Ok` instead. In case the result is an error, this function returns - /// `Ok(-1)` and sets the last OS error accordingly. - /// - /// 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>( - &mut self, - result: std::io::Result, - ) -> InterpResult<'tcx, T> { - match result { - Ok(ok) => Ok(ok), - Err(e) => { - self.eval_context_mut().set_last_error_from_io_error(e)?; - Ok((-1).into()) - } - } - } - /// Dereference a pointer operand to a place using `layout` instead of the pointer's declared type fn deref_pointer_as( &self, diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 78e7bf704552d..c4adf38299c81 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -150,6 +150,7 @@ pub use crate::range_map::RangeMap; pub use crate::shims::EmulateItemResult; pub use crate::shims::env::{EnvVars, EvalContextExt as _}; pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _}; +pub use crate::shims::io_error::EvalContextExt as _; pub use crate::shims::os_str::EvalContextExt as _; pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _}; pub use crate::shims::time::EvalContextExt as _; diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs new file mode 100644 index 0000000000000..8c727f4c894ed --- /dev/null +++ b/src/tools/miri/src/shims/io_error.rs @@ -0,0 +1,190 @@ +use std::io; + +use crate::*; + +// This mapping should match `decode_error_kind` in +// . +const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { + use std::io::ErrorKind::*; + &[ + ("E2BIG", ArgumentListTooLong), + ("EADDRINUSE", AddrInUse), + ("EADDRNOTAVAIL", AddrNotAvailable), + ("EBUSY", ResourceBusy), + ("ECONNABORTED", ConnectionAborted), + ("ECONNREFUSED", ConnectionRefused), + ("ECONNRESET", ConnectionReset), + ("EDEADLK", Deadlock), + ("EDQUOT", FilesystemQuotaExceeded), + ("EEXIST", AlreadyExists), + ("EFBIG", FileTooLarge), + ("EHOSTUNREACH", HostUnreachable), + ("EINTR", Interrupted), + ("EINVAL", InvalidInput), + ("EISDIR", IsADirectory), + ("ELOOP", FilesystemLoop), + ("ENOENT", NotFound), + ("ENOMEM", OutOfMemory), + ("ENOSPC", StorageFull), + ("ENOSYS", Unsupported), + ("EMLINK", TooManyLinks), + ("ENAMETOOLONG", InvalidFilename), + ("ENETDOWN", NetworkDown), + ("ENETUNREACH", NetworkUnreachable), + ("ENOTCONN", NotConnected), + ("ENOTDIR", NotADirectory), + ("ENOTEMPTY", DirectoryNotEmpty), + ("EPIPE", BrokenPipe), + ("EROFS", ReadOnlyFilesystem), + ("ESPIPE", NotSeekable), + ("ESTALE", StaleNetworkFileHandle), + ("ETIMEDOUT", TimedOut), + ("ETXTBSY", ExecutableFileBusy), + ("EXDEV", CrossesDevices), + // The following have two valid options. We have both for the forwards mapping; only the + // first one will be used for the backwards mapping. + ("EPERM", PermissionDenied), + ("EACCES", PermissionDenied), + ("EWOULDBLOCK", WouldBlock), + ("EAGAIN", WouldBlock), + ] +}; +// This mapping should match `decode_error_kind` in +// . +const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { + use std::io::ErrorKind::*; + // FIXME: this is still incomplete. + &[ + ("ERROR_ACCESS_DENIED", PermissionDenied), + ("ERROR_FILE_NOT_FOUND", NotFound), + ("ERROR_INVALID_PARAMETER", InvalidInput), + ] +}; + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Get last error variable as a place, lazily allocating thread-local storage for it if + /// necessary. + fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx>> { + let this = self.eval_context_mut(); + if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() { + Ok(errno_place.clone()) + } else { + // Allocate new place, set initial value to 0. + let errno_layout = this.machine.layouts.u32; + let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into())?; + this.write_scalar(Scalar::from_u32(0), &errno_place)?; + this.active_thread_mut().last_error = Some(errno_place.clone()); + Ok(errno_place) + } + } + + /// Sets the last error variable. + fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let errno_place = this.last_error_place()?; + this.write_scalar(scalar, &errno_place) + } + + /// Gets the last error variable. + fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + let errno_place = this.last_error_place()?; + this.read_scalar(&errno_place) + } + + /// This function tries to produce the most similar OS error from the `std::io::ErrorKind` + /// as a platform-specific errnum. + fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_ref(); + let target = &this.tcx.sess.target; + if target.families.iter().any(|f| f == "unix") { + for &(name, kind) in UNIX_IO_ERROR_TABLE { + if err.kind() == kind { + return Ok(this.eval_libc(name)); + } + } + throw_unsup_format!("unsupported io error: {err}") + } else if target.families.iter().any(|f| f == "windows") { + for &(name, kind) in WINDOWS_IO_ERROR_TABLE { + if err.kind() == kind { + return Ok(this.eval_windows("c", name)); + } + } + throw_unsup_format!("unsupported io error: {err}"); + } else { + throw_unsup_format!( + "converting io::Error into errnum is unsupported for OS {}", + target.os + ) + } + } + + /// The inverse of `io_error_to_errnum`. + #[allow(clippy::needless_return)] + fn try_errnum_to_io_error( + &self, + errnum: Scalar, + ) -> InterpResult<'tcx, Option> { + let this = self.eval_context_ref(); + let target = &this.tcx.sess.target; + if target.families.iter().any(|f| f == "unix") { + let errnum = errnum.to_i32()?; + for &(name, kind) in UNIX_IO_ERROR_TABLE { + if errnum == this.eval_libc_i32(name) { + return Ok(Some(kind)); + } + } + return Ok(None); + } else if target.families.iter().any(|f| f == "windows") { + let errnum = errnum.to_u32()?; + for &(name, kind) in WINDOWS_IO_ERROR_TABLE { + if errnum == this.eval_windows("c", name).to_u32()? { + return Ok(Some(kind)); + } + } + return Ok(None); + } else { + throw_unsup_format!( + "converting errnum into io::Error is unsupported for OS {}", + target.os + ) + } + } + + /// Sets the last OS error using a `std::io::ErrorKind`. + fn set_last_error_from_io_error(&mut self, err: std::io::Error) -> InterpResult<'tcx> { + self.set_last_error(self.io_error_to_errnum(err)?) + } + + /// Sets the last OS error using a `std::io::ErrorKind` and writes -1 to dest place. + fn set_last_error_and_return( + &mut self, + err: impl Into, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + this.set_last_error(this.io_error_to_errnum(err.into())?)?; + this.write_int(-1, dest)?; + Ok(()) + } + + /// Helper function that consumes an `std::io::Result` and returns an + /// `InterpResult<'tcx,T>::Ok` instead. In case the result is an error, this function returns + /// `Ok(-1)` and sets the last OS error accordingly. + /// + /// 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>( + &mut self, + result: std::io::Result, + ) -> InterpResult<'tcx, T> { + match result { + Ok(ok) => Ok(ok), + Err(e) => { + self.eval_context_mut().set_last_error_from_io_error(e)?; + Ok((-1).into()) + } + } + } +} diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index a689ac2b3784e..b9317ac1a15fa 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -12,6 +12,7 @@ mod x86; pub mod env; pub mod extern_static; pub mod foreign_items; +pub mod io_error; pub mod os_str; pub mod panic; pub mod time; From 9c21fd4b93540d841e11ba4f975660666a9bfc3f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 1 Oct 2024 09:02:33 +0200 Subject: [PATCH 2/3] make set_last_error directly callable on a bunch of ways to represent errors --- src/tools/miri/src/lib.rs | 2 +- src/tools/miri/src/shims/io_error.rs | 48 +++++++++++++++---- src/tools/miri/src/shims/unix/env.rs | 15 +++--- src/tools/miri/src/shims/unix/fd.rs | 8 ++-- .../miri/src/shims/unix/foreign_items.rs | 22 ++++----- src/tools/miri/src/shims/unix/fs.rs | 48 ++++++++----------- src/tools/miri/src/shims/unix/linux/epoll.rs | 6 +-- src/tools/miri/src/shims/unix/linux/sync.rs | 9 ++-- src/tools/miri/src/shims/windows/env.rs | 8 ++-- .../miri/src/shims/windows/foreign_items.rs | 2 +- 10 files changed, 88 insertions(+), 80 deletions(-) diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index c4adf38299c81..330147c8f1cf8 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -150,7 +150,7 @@ pub use crate::range_map::RangeMap; pub use crate::shims::EmulateItemResult; pub use crate::shims::env::{EnvVars, EvalContextExt as _}; pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _}; -pub use crate::shims::io_error::EvalContextExt as _; +pub use crate::shims::io_error::{EvalContextExt as _, LibcError}; pub use crate::shims::os_str::EvalContextExt as _; pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _}; pub use crate::shims::time::EvalContextExt as _; diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs index 8c727f4c894ed..61b943b599d93 100644 --- a/src/tools/miri/src/shims/io_error.rs +++ b/src/tools/miri/src/shims/io_error.rs @@ -2,6 +2,34 @@ use std::io; use crate::*; +/// A representation of an IO error: either a libc error name, +/// or a host error. +#[derive(Debug)] +pub enum IoError { + LibcError(&'static str), + HostError(io::Error), + Raw(Scalar), +} +pub use self::IoError::*; + +impl From for IoError { + fn from(value: io::Error) -> Self { + IoError::HostError(value) + } +} + +impl From for IoError { + fn from(value: io::ErrorKind) -> Self { + IoError::HostError(value.into()) + } +} + +impl From for IoError { + fn from(value: Scalar) -> Self { + IoError::Raw(value) + } +} + // This mapping should match `decode_error_kind` in // . const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { @@ -80,10 +108,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } /// Sets the last error variable. - fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { + fn set_last_error(&mut self, err: impl Into) -> InterpResult<'tcx> { let this = self.eval_context_mut(); + let errno = match err.into() { + HostError(err) => this.io_error_to_errnum(err)?, + LibcError(name) => this.eval_libc(name), + Raw(val) => val, + }; let errno_place = this.last_error_place()?; - this.write_scalar(scalar, &errno_place) + this.write_scalar(errno, &errno_place) } /// Gets the last error variable. @@ -152,19 +185,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - /// Sets the last OS error using a `std::io::ErrorKind`. - fn set_last_error_from_io_error(&mut self, err: std::io::Error) -> InterpResult<'tcx> { - self.set_last_error(self.io_error_to_errnum(err)?) - } - /// Sets the last OS error using a `std::io::ErrorKind` and writes -1 to dest place. fn set_last_error_and_return( &mut self, - err: impl Into, + err: impl Into, dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - this.set_last_error(this.io_error_to_errnum(err.into())?)?; + this.set_last_error(err)?; this.write_int(-1, dest)?; Ok(()) } @@ -182,7 +210,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match result { Ok(ok) => Ok(ok), Err(e) => { - self.eval_context_mut().set_last_error_from_io_error(e)?; + self.eval_context_mut().set_last_error(e)?; Ok((-1).into()) } } diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs index 184a6c238b392..923de15d2dafa 100644 --- a/src/tools/miri/src/shims/unix/env.rs +++ b/src/tools/miri/src/shims/unix/env.rs @@ -177,8 +177,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_i32(0)) // return zero on success } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; Ok(Scalar::from_i32(-1)) } } @@ -203,8 +202,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_i32(0)) } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; Ok(Scalar::from_i32(-1)) } } @@ -218,7 +216,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`getcwd`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Pointer::null()); } @@ -228,10 +226,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.write_path_to_c_str(&cwd, buf, size)?.0 { return Ok(buf); } - let erange = this.eval_libc("ERANGE"); - this.set_last_error(erange)?; + this.set_last_error(LibcError("ERANGE"))?; } - Err(e) => this.set_last_error_from_io_error(e)?, + Err(e) => this.set_last_error(e)?, } Ok(Pointer::null()) @@ -245,7 +242,7 @@ 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())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index f64d0ed1467bc..fe634de122c46 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -524,7 +524,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("`fcntl`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -606,8 +606,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { 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)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_int(-1, dest)?; return Ok(()); }; @@ -651,8 +650,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { 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)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_int(-1, dest)?; return Ok(()); }; diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 528d068ea92c0..14af4245148e2 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -355,8 +355,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=reallocarray match this.compute_size_in_bytes(Size::from_bytes(size), nmemb) { None => { - let einval = this.eval_libc("ENOMEM"); - this.set_last_error(einval)?; + let enmem = this.eval_libc("ENOMEM"); + this.set_last_error(enmem)?; this.write_null(dest)?; } Some(len) => { @@ -646,13 +646,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let chunk_size = CpuAffinityMask::chunk_size(this); if this.ptr_is_null(mask)? { - let einval = this.eval_libc("EFAULT"); - this.set_last_error(einval)?; + let efault = this.eval_libc("EFAULT"); + this.set_last_error(efault)?; this.write_int(-1, dest)?; } else if cpusetsize == 0 || cpusetsize.checked_rem(chunk_size).unwrap() != 0 { // we only copy whole chunks of size_of::() - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_int(-1, dest)?; } else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) { let cpuset = cpuset.clone(); @@ -662,8 +661,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } else { // The thread whose ID is pid could not be found - let einval = this.eval_libc("ESRCH"); - this.set_last_error(einval)?; + let esrch = this.eval_libc("ESRCH"); + this.set_last_error(esrch)?; this.write_int(-1, dest)?; } } @@ -689,8 +688,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; if this.ptr_is_null(mask)? { - let einval = this.eval_libc("EFAULT"); - this.set_last_error(einval)?; + let efault = this.eval_libc("EFAULT"); + this.set_last_error(efault)?; this.write_int(-1, dest)?; } else { // NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`. @@ -707,8 +706,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } None => { // The intersection between the mask and the available CPUs was empty. - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_int(-1, dest)?; } } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 2fa1729ad9c8a..80e45d8439819 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -534,8 +534,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let o_tmpfile = this.eval_libc_i32("O_TMPFILE"); if flag & o_tmpfile == o_tmpfile { // if the flag contains `O_TMPFILE` then we return a graceful error - let eopnotsupp = this.eval_libc("EOPNOTSUPP"); - this.set_last_error(eopnotsupp)?; + this.set_last_error(LibcError("EOPNOTSUPP"))?; return Ok(Scalar::from_i32(-1)); } } @@ -572,7 +571,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("`open`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -591,8 +590,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let seek_from = if whence == this.eval_libc_i32("SEEK_SET") { if offset < 0 { // Negative offsets return `EINVAL`. - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; return Ok(Scalar::from_i64(-1)); } else { SeekFrom::Start(u64::try_from(offset).unwrap()) @@ -602,8 +600,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else if whence == this.eval_libc_i32("SEEK_END") { SeekFrom::End(i64::try_from(offset).unwrap()) } else { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; return Ok(Scalar::from_i64(-1)); }; @@ -627,7 +624,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("`unlink`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -658,7 +655,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("`symlink`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -959,7 +956,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("`rename`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -983,7 +980,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())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -1011,7 +1008,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("`rmdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_i32(-1)); } @@ -1045,7 +1042,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_target_usize(id, this)) } Err(e) => { - this.set_last_error_from_io_error(e)?; + this.set_last_error(e)?; Ok(Scalar::null_ptr(this)) } } @@ -1130,7 +1127,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None } Some(Err(e)) => { - this.set_last_error_from_io_error(e)?; + this.set_last_error(e)?; None } }; @@ -1314,15 +1311,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_i32(result)) } else { drop(fd); - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; Ok(Scalar::from_i32(-1)) } } else { drop(fd); // The file is not writable - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; Ok(Scalar::from_i32(-1)) } } @@ -1400,16 +1395,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let flags = this.read_scalar(flags_op)?.to_i32()?; if offset < 0 || nbytes < 0 { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; return Ok(Scalar::from_i32(-1)); } let allowed_flags = this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_BEFORE") | this.eval_libc_i32("SYNC_FILE_RANGE_WRITE") | this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_AFTER"); if flags & allowed_flags != flags { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; return Ok(Scalar::from_i32(-1)); } @@ -1471,7 +1464,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(path_bytes.len().try_into().unwrap()) } Err(e) => { - this.set_last_error_from_io_error(e)?; + this.set_last_error(e)?; Ok(-1) } } @@ -1551,7 +1544,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_maybe_pointer(dest, this)) } Err(e) => { - this.set_last_error_from_io_error(e)?; + this.set_last_error(e)?; Ok(Scalar::from_target_usize(0, this)) } } @@ -1603,8 +1596,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // If we don't find the suffix, it is an error. if last_six_char_bytes != suffix_bytes { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; return Ok(Scalar::from_i32(-1)); } @@ -1670,7 +1662,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => { // "On error, -1 is returned, and errno is set to // indicate the error" - this.set_last_error_from_io_error(e)?; + this.set_last_error(e)?; return Ok(Scalar::from_i32(-1)); } }, @@ -1749,7 +1741,7 @@ impl FileMetadata { let metadata = match metadata { Ok(metadata) => metadata, Err(e) => { - ecx.set_last_error_from_io_error(e)?; + ecx.set_last_error(e)?; return Ok(None); } }; diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index 675a404ae117c..ff0709dd5ac1c 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -261,8 +261,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Throw EINVAL if epfd and fd have the same value. if epfd_value == fd { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; return Ok(Scalar::from_i32(-1)); } @@ -443,8 +442,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let timeout = this.read_scalar(timeout)?.to_i32()?; if epfd_value <= 0 || maxevents <= 0 { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_int(-1, dest)?; return Ok(()); } diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs index bd21203907419..1d1764b3a1a11 100644 --- a/src/tools/miri/src/shims/unix/linux/sync.rs +++ b/src/tools/miri/src/shims/unix/linux/sync.rs @@ -75,8 +75,7 @@ pub fn futex<'tcx>( }; if bitset == 0 { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; return Ok(()); } @@ -88,8 +87,7 @@ pub fn futex<'tcx>( let duration = match this.read_timespec(&timeout)? { Some(duration) => duration, None => { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; return Ok(()); } @@ -198,8 +196,7 @@ pub fn futex<'tcx>( u32::MAX }; if bitset == 0 { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; + this.set_last_error(LibcError("EINVAL"))?; this.write_scalar(Scalar::from_target_isize(-1, this), dest)?; return Ok(()); } diff --git a/src/tools/miri/src/shims/windows/env.rs b/src/tools/miri/src/shims/windows/env.rs index 9707482b1e2c2..cf99ac758c643 100644 --- a/src/tools/miri/src/shims/windows/env.rs +++ b/src/tools/miri/src/shims/windows/env.rs @@ -150,7 +150,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(Scalar::from_u32(0)); } @@ -163,7 +163,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_path_to_wide_str(&cwd, buf, size)?, ))); } - Err(e) => this.set_last_error_from_io_error(e)?, + Err(e) => this.set_last_error(e)?, } Ok(Scalar::from_u32(0)) } @@ -182,7 +182,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; + this.set_last_error(ErrorKind::PermissionDenied)?; return Ok(this.eval_windows("c", "FALSE")); } @@ -190,7 +190,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match env::set_current_dir(path) { Ok(()) => Ok(this.eval_windows("c", "TRUE")), Err(e) => { - this.set_last_error_from_io_error(e)?; + this.set_last_error(e)?; Ok(this.eval_windows("c", "FALSE")) } } diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 22634c509bed0..c51726b05dc5a 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -227,7 +227,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let filename = this.read_path_from_wide_str(filename)?; let result = match win_absolute(&filename)? { Err(err) => { - this.set_last_error_from_io_error(err)?; + this.set_last_error(err)?; Scalar::from_u32(0) // return zero upon failure } Ok(abs_filename) => { From 4f4e1d42b59e96141745ace47ed7a7c96211bf33 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 1 Oct 2024 09:08:10 +0200 Subject: [PATCH 3/3] add set_last_error_and_return_i32 helper and use it in a few places --- src/tools/miri/src/shims/io_error.rs | 34 ++++++++++++++++++---------- src/tools/miri/src/shims/unix/env.rs | 10 +++----- src/tools/miri/src/shims/unix/fd.rs | 11 +++------ 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs index 61b943b599d93..aa13c405145cc 100644 --- a/src/tools/miri/src/shims/io_error.rs +++ b/src/tools/miri/src/shims/io_error.rs @@ -119,6 +119,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(errno, &errno_place) } + /// Sets the last OS error and writes -1 to dest place. + fn set_last_error_and_return( + &mut self, + err: impl Into, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + this.set_last_error(err)?; + this.write_int(-1, dest)?; + Ok(()) + } + + /// Sets the last OS error and return `-1` as a `i32`-typed Scalar + fn set_last_error_and_return_i32( + &mut self, + err: impl Into, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + this.set_last_error(err)?; + Ok(Scalar::from_i32(-1)) + } + /// Gets the last error variable. fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); @@ -185,18 +207,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - /// Sets the last OS error using a `std::io::ErrorKind` and writes -1 to dest place. - fn set_last_error_and_return( - &mut self, - err: impl Into, - dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - this.set_last_error(err)?; - this.write_int(-1, dest)?; - Ok(()) - } - /// Helper function that consumes an `std::io::Result` and returns an /// `InterpResult<'tcx,T>::Ok` instead. In case the result is an error, this function returns /// `Ok(-1)` and sets the last OS error accordingly. diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs index 923de15d2dafa..4a9e2313aadf1 100644 --- a/src/tools/miri/src/shims/unix/env.rs +++ b/src/tools/miri/src/shims/unix/env.rs @@ -177,8 +177,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_i32(0)) // return zero on success } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. - this.set_last_error(LibcError("EINVAL"))?; - Ok(Scalar::from_i32(-1)) + this.set_last_error_and_return_i32(LibcError("EINVAL")) } } @@ -202,8 +201,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_i32(0)) } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. - this.set_last_error(LibcError("EINVAL"))?; - Ok(Scalar::from_i32(-1)) + this.set_last_error_and_return_i32(LibcError("EINVAL")) } } @@ -242,9 +240,7 @@ 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(ErrorKind::PermissionDenied)?; - - return Ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied); } let result = env::set_current_dir(path).map(|()| 0); diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index fe634de122c46..41a819fb31f8c 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -524,8 +524,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("`fcntl`", reject_with)?; - this.set_last_error(ErrorKind::PermissionDenied)?; - return Ok(Scalar::from_i32(-1)); + return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied); } this.ffullsync_fd(fd_num) @@ -606,9 +605,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None => fd.read(&fd, communicate, buf, count, dest, this)?, Some(offset) => { let Ok(offset) = u64::try_from(offset) else { - this.set_last_error(LibcError("EINVAL"))?; - this.write_int(-1, dest)?; - return Ok(()); + return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; fd.pread(communicate, offset, buf, count, dest, this)? } @@ -650,9 +647,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None => fd.write(&fd, communicate, buf, count, dest, this)?, Some(offset) => { let Ok(offset) = u64::try_from(offset) else { - this.set_last_error(LibcError("EINVAL"))?; - this.write_int(-1, dest)?; - return Ok(()); + return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; fd.pwrite(communicate, buf, count, offset, dest, this)? }