Auto merge of #3929 - RalfJung:io-error, r=RalfJung
Make returning io errors more uniform and convenient
This commit is contained in:
commit
97510cd9dc
@ -2,7 +2,7 @@ use std::collections::BTreeSet;
|
|||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{cmp, io, iter};
|
use std::{cmp, iter};
|
||||||
|
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use rustc_apfloat::Float;
|
use rustc_apfloat::Float;
|
||||||
@ -31,65 +31,6 @@ pub enum AccessKind {
|
|||||||
Write,
|
Write,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This mapping should match `decode_error_kind` in
|
|
||||||
// <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/unix/mod.rs>.
|
|
||||||
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
|
|
||||||
// <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/windows/mod.rs>.
|
|
||||||
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.
|
/// Gets an instance for a path.
|
||||||
///
|
///
|
||||||
/// A `None` namespace indicates we are looking for a module.
|
/// 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")
|
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<std::io::ErrorKind>> {
|
|
||||||
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<io::Error>,
|
|
||||||
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<T>` 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<i32>` instead of `i32` directly because some IO related
|
|
||||||
/// functions return different integer types (like `read`, that returns an `i64`).
|
|
||||||
fn try_unwrap_io_result<T: From<i32>>(
|
|
||||||
&mut self,
|
|
||||||
result: std::io::Result<T>,
|
|
||||||
) -> 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
|
/// Dereference a pointer operand to a place using `layout` instead of the pointer's declared type
|
||||||
fn deref_pointer_as(
|
fn deref_pointer_as(
|
||||||
&self,
|
&self,
|
||||||
|
@ -150,6 +150,7 @@ pub use crate::range_map::RangeMap;
|
|||||||
pub use crate::shims::EmulateItemResult;
|
pub use crate::shims::EmulateItemResult;
|
||||||
pub use crate::shims::env::{EnvVars, EvalContextExt as _};
|
pub use crate::shims::env::{EnvVars, EvalContextExt as _};
|
||||||
pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _};
|
pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _};
|
||||||
|
pub use crate::shims::io_error::{EvalContextExt as _, LibcError};
|
||||||
pub use crate::shims::os_str::EvalContextExt as _;
|
pub use crate::shims::os_str::EvalContextExt as _;
|
||||||
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
|
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
|
||||||
pub use crate::shims::time::EvalContextExt as _;
|
pub use crate::shims::time::EvalContextExt as _;
|
||||||
|
228
src/tools/miri/src/shims/io_error.rs
Normal file
228
src/tools/miri/src/shims/io_error.rs
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
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<io::Error> for IoError {
|
||||||
|
fn from(value: io::Error) -> Self {
|
||||||
|
IoError::HostError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::ErrorKind> for IoError {
|
||||||
|
fn from(value: io::ErrorKind) -> Self {
|
||||||
|
IoError::HostError(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Scalar> for IoError {
|
||||||
|
fn from(value: Scalar) -> Self {
|
||||||
|
IoError::Raw(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This mapping should match `decode_error_kind` in
|
||||||
|
// <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/unix/mod.rs>.
|
||||||
|
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
|
||||||
|
// <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/windows/mod.rs>.
|
||||||
|
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, err: impl Into<IoError>) -> 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(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<IoError>,
|
||||||
|
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<IoError>,
|
||||||
|
) -> 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();
|
||||||
|
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<std::io::ErrorKind>> {
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function that consumes an `std::io::Result<T>` 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<i32>` instead of `i32` directly because some IO related
|
||||||
|
/// functions return different integer types (like `read`, that returns an `i64`).
|
||||||
|
fn try_unwrap_io_result<T: From<i32>>(
|
||||||
|
&mut self,
|
||||||
|
result: std::io::Result<T>,
|
||||||
|
) -> InterpResult<'tcx, T> {
|
||||||
|
match result {
|
||||||
|
Ok(ok) => Ok(ok),
|
||||||
|
Err(e) => {
|
||||||
|
self.eval_context_mut().set_last_error(e)?;
|
||||||
|
Ok((-1).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ mod x86;
|
|||||||
pub mod env;
|
pub mod env;
|
||||||
pub mod extern_static;
|
pub mod extern_static;
|
||||||
pub mod foreign_items;
|
pub mod foreign_items;
|
||||||
|
pub mod io_error;
|
||||||
pub mod os_str;
|
pub mod os_str;
|
||||||
pub mod panic;
|
pub mod panic;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
@ -177,9 +177,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
Ok(Scalar::from_i32(0)) // return zero on success
|
Ok(Scalar::from_i32(0)) // return zero on success
|
||||||
} else {
|
} else {
|
||||||
// name argument is a null pointer, points to an empty string, or points to a string containing an '=' character.
|
// 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_and_return_i32(LibcError("EINVAL"))
|
||||||
this.set_last_error(einval)?;
|
|
||||||
Ok(Scalar::from_i32(-1))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,9 +201,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
Ok(Scalar::from_i32(0))
|
Ok(Scalar::from_i32(0))
|
||||||
} else {
|
} else {
|
||||||
// name argument is a null pointer, points to an empty string, or points to a string containing an '=' character.
|
// 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_and_return_i32(LibcError("EINVAL"))
|
||||||
this.set_last_error(einval)?;
|
|
||||||
Ok(Scalar::from_i32(-1))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +214,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||||
this.reject_in_isolation("`getcwd`", reject_with)?;
|
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());
|
return Ok(Pointer::null());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,10 +224,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
if this.write_path_to_c_str(&cwd, buf, size)?.0 {
|
if this.write_path_to_c_str(&cwd, buf, size)?.0 {
|
||||||
return Ok(buf);
|
return Ok(buf);
|
||||||
}
|
}
|
||||||
let erange = this.eval_libc("ERANGE");
|
this.set_last_error(LibcError("ERANGE"))?;
|
||||||
this.set_last_error(erange)?;
|
|
||||||
}
|
}
|
||||||
Err(e) => this.set_last_error_from_io_error(e)?,
|
Err(e) => this.set_last_error(e)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Pointer::null())
|
Ok(Pointer::null())
|
||||||
@ -245,9 +240,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||||
this.reject_in_isolation("`chdir`", reject_with)?;
|
this.reject_in_isolation("`chdir`", reject_with)?;
|
||||||
this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
|
return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
|
||||||
|
|
||||||
return Ok(Scalar::from_i32(-1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = env::set_current_dir(path).map(|()| 0);
|
let result = env::set_current_dir(path).map(|()| 0);
|
||||||
|
@ -524,8 +524,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Reject if isolation is enabled.
|
// Reject if isolation is enabled.
|
||||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||||
this.reject_in_isolation("`fcntl`", reject_with)?;
|
this.reject_in_isolation("`fcntl`", reject_with)?;
|
||||||
this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
|
return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
|
||||||
return Ok(Scalar::from_i32(-1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ffullsync_fd(fd_num)
|
this.ffullsync_fd(fd_num)
|
||||||
@ -606,10 +605,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
None => fd.read(&fd, communicate, buf, count, dest, 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");
|
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
|
||||||
this.set_last_error(einval)?;
|
|
||||||
this.write_int(-1, dest)?;
|
|
||||||
return Ok(());
|
|
||||||
};
|
};
|
||||||
fd.pread(communicate, offset, buf, count, dest, this)?
|
fd.pread(communicate, offset, buf, count, dest, this)?
|
||||||
}
|
}
|
||||||
@ -651,10 +647,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
None => fd.write(&fd, communicate, buf, count, dest, 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");
|
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
|
||||||
this.set_last_error(einval)?;
|
|
||||||
this.write_int(-1, dest)?;
|
|
||||||
return Ok(());
|
|
||||||
};
|
};
|
||||||
fd.pwrite(communicate, buf, count, offset, dest, this)?
|
fd.pwrite(communicate, buf, count, offset, dest, this)?
|
||||||
}
|
}
|
||||||
|
@ -355,8 +355,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=reallocarray
|
// FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=reallocarray
|
||||||
match this.compute_size_in_bytes(Size::from_bytes(size), nmemb) {
|
match this.compute_size_in_bytes(Size::from_bytes(size), nmemb) {
|
||||||
None => {
|
None => {
|
||||||
let einval = this.eval_libc("ENOMEM");
|
let enmem = this.eval_libc("ENOMEM");
|
||||||
this.set_last_error(einval)?;
|
this.set_last_error(enmem)?;
|
||||||
this.write_null(dest)?;
|
this.write_null(dest)?;
|
||||||
}
|
}
|
||||||
Some(len) => {
|
Some(len) => {
|
||||||
@ -646,13 +646,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let chunk_size = CpuAffinityMask::chunk_size(this);
|
let chunk_size = CpuAffinityMask::chunk_size(this);
|
||||||
|
|
||||||
if this.ptr_is_null(mask)? {
|
if this.ptr_is_null(mask)? {
|
||||||
let einval = this.eval_libc("EFAULT");
|
let efault = this.eval_libc("EFAULT");
|
||||||
this.set_last_error(einval)?;
|
this.set_last_error(efault)?;
|
||||||
this.write_int(-1, dest)?;
|
this.write_int(-1, dest)?;
|
||||||
} else if cpusetsize == 0 || cpusetsize.checked_rem(chunk_size).unwrap() != 0 {
|
} else if cpusetsize == 0 || cpusetsize.checked_rem(chunk_size).unwrap() != 0 {
|
||||||
// we only copy whole chunks of size_of::<c_ulong>()
|
// we only copy whole chunks of size_of::<c_ulong>()
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
this.write_int(-1, dest)?;
|
this.write_int(-1, dest)?;
|
||||||
} else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) {
|
} else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) {
|
||||||
let cpuset = cpuset.clone();
|
let cpuset = cpuset.clone();
|
||||||
@ -662,8 +661,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_null(dest)?;
|
this.write_null(dest)?;
|
||||||
} else {
|
} else {
|
||||||
// The thread whose ID is pid could not be found
|
// The thread whose ID is pid could not be found
|
||||||
let einval = this.eval_libc("ESRCH");
|
let esrch = this.eval_libc("ESRCH");
|
||||||
this.set_last_error(einval)?;
|
this.set_last_error(esrch)?;
|
||||||
this.write_int(-1, dest)?;
|
this.write_int(-1, dest)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -689,8 +688,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if this.ptr_is_null(mask)? {
|
if this.ptr_is_null(mask)? {
|
||||||
let einval = this.eval_libc("EFAULT");
|
let efault = this.eval_libc("EFAULT");
|
||||||
this.set_last_error(einval)?;
|
this.set_last_error(efault)?;
|
||||||
this.write_int(-1, dest)?;
|
this.write_int(-1, dest)?;
|
||||||
} else {
|
} else {
|
||||||
// NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`.
|
// NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`.
|
||||||
@ -707,8 +706,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// The intersection between the mask and the available CPUs was empty.
|
// The intersection between the mask and the available CPUs was empty.
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
this.write_int(-1, dest)?;
|
this.write_int(-1, dest)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -534,8 +534,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let o_tmpfile = this.eval_libc_i32("O_TMPFILE");
|
let o_tmpfile = this.eval_libc_i32("O_TMPFILE");
|
||||||
if flag & o_tmpfile == o_tmpfile {
|
if flag & o_tmpfile == o_tmpfile {
|
||||||
// if the flag contains `O_TMPFILE` then we return a graceful error
|
// if the flag contains `O_TMPFILE` then we return a graceful error
|
||||||
let eopnotsupp = this.eval_libc("EOPNOTSUPP");
|
this.set_last_error(LibcError("EOPNOTSUPP"))?;
|
||||||
this.set_last_error(eopnotsupp)?;
|
|
||||||
return Ok(Scalar::from_i32(-1));
|
return Ok(Scalar::from_i32(-1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -572,7 +571,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Reject if isolation is enabled.
|
// Reject if isolation is enabled.
|
||||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||||
this.reject_in_isolation("`open`", reject_with)?;
|
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));
|
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") {
|
let seek_from = if whence == this.eval_libc_i32("SEEK_SET") {
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
// Negative offsets return `EINVAL`.
|
// Negative offsets return `EINVAL`.
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
return Ok(Scalar::from_i64(-1));
|
return Ok(Scalar::from_i64(-1));
|
||||||
} else {
|
} else {
|
||||||
SeekFrom::Start(u64::try_from(offset).unwrap())
|
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") {
|
} else if whence == this.eval_libc_i32("SEEK_END") {
|
||||||
SeekFrom::End(i64::try_from(offset).unwrap())
|
SeekFrom::End(i64::try_from(offset).unwrap())
|
||||||
} else {
|
} else {
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
return Ok(Scalar::from_i64(-1));
|
return Ok(Scalar::from_i64(-1));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -627,7 +624,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Reject if isolation is enabled.
|
// Reject if isolation is enabled.
|
||||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||||
this.reject_in_isolation("`unlink`", reject_with)?;
|
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));
|
return Ok(Scalar::from_i32(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -658,7 +655,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Reject if isolation is enabled.
|
// Reject if isolation is enabled.
|
||||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||||
this.reject_in_isolation("`symlink`", reject_with)?;
|
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));
|
return Ok(Scalar::from_i32(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -959,7 +956,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Reject if isolation is enabled.
|
// Reject if isolation is enabled.
|
||||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||||
this.reject_in_isolation("`rename`", reject_with)?;
|
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));
|
return Ok(Scalar::from_i32(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -983,7 +980,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Reject if isolation is enabled.
|
// Reject if isolation is enabled.
|
||||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||||
this.reject_in_isolation("`mkdir`", reject_with)?;
|
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));
|
return Ok(Scalar::from_i32(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1011,7 +1008,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Reject if isolation is enabled.
|
// Reject if isolation is enabled.
|
||||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||||
this.reject_in_isolation("`rmdir`", reject_with)?;
|
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));
|
return Ok(Scalar::from_i32(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1045,7 +1042,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
Ok(Scalar::from_target_usize(id, this))
|
Ok(Scalar::from_target_usize(id, this))
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
this.set_last_error_from_io_error(e)?;
|
this.set_last_error(e)?;
|
||||||
Ok(Scalar::null_ptr(this))
|
Ok(Scalar::null_ptr(this))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1130,7 +1127,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
Some(Err(e)) => {
|
Some(Err(e)) => {
|
||||||
this.set_last_error_from_io_error(e)?;
|
this.set_last_error(e)?;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1314,15 +1311,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
Ok(Scalar::from_i32(result))
|
Ok(Scalar::from_i32(result))
|
||||||
} else {
|
} else {
|
||||||
drop(fd);
|
drop(fd);
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
Ok(Scalar::from_i32(-1))
|
Ok(Scalar::from_i32(-1))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
drop(fd);
|
drop(fd);
|
||||||
// The file is not writable
|
// The file is not writable
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
Ok(Scalar::from_i32(-1))
|
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()?;
|
let flags = this.read_scalar(flags_op)?.to_i32()?;
|
||||||
|
|
||||||
if offset < 0 || nbytes < 0 {
|
if offset < 0 || nbytes < 0 {
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
return Ok(Scalar::from_i32(-1));
|
return Ok(Scalar::from_i32(-1));
|
||||||
}
|
}
|
||||||
let allowed_flags = this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_BEFORE")
|
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_WRITE")
|
||||||
| this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_AFTER");
|
| this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_AFTER");
|
||||||
if flags & allowed_flags != flags {
|
if flags & allowed_flags != flags {
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
return Ok(Scalar::from_i32(-1));
|
return Ok(Scalar::from_i32(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1471,7 +1464,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
Ok(path_bytes.len().try_into().unwrap())
|
Ok(path_bytes.len().try_into().unwrap())
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
this.set_last_error_from_io_error(e)?;
|
this.set_last_error(e)?;
|
||||||
Ok(-1)
|
Ok(-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1551,7 +1544,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
Ok(Scalar::from_maybe_pointer(dest, this))
|
Ok(Scalar::from_maybe_pointer(dest, this))
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
this.set_last_error_from_io_error(e)?;
|
this.set_last_error(e)?;
|
||||||
Ok(Scalar::from_target_usize(0, this))
|
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 we don't find the suffix, it is an error.
|
||||||
if last_six_char_bytes != suffix_bytes {
|
if last_six_char_bytes != suffix_bytes {
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
return Ok(Scalar::from_i32(-1));
|
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
|
// "On error, -1 is returned, and errno is set to
|
||||||
// indicate the error"
|
// indicate the error"
|
||||||
this.set_last_error_from_io_error(e)?;
|
this.set_last_error(e)?;
|
||||||
return Ok(Scalar::from_i32(-1));
|
return Ok(Scalar::from_i32(-1));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1749,7 +1741,7 @@ impl FileMetadata {
|
|||||||
let metadata = match metadata {
|
let metadata = match metadata {
|
||||||
Ok(metadata) => metadata,
|
Ok(metadata) => metadata,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
ecx.set_last_error_from_io_error(e)?;
|
ecx.set_last_error(e)?;
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -261,8 +261,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// Throw EINVAL if epfd and fd have the same value.
|
// Throw EINVAL if epfd and fd have the same value.
|
||||||
if epfd_value == fd {
|
if epfd_value == fd {
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
return Ok(Scalar::from_i32(-1));
|
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()?;
|
let timeout = this.read_scalar(timeout)?.to_i32()?;
|
||||||
|
|
||||||
if epfd_value <= 0 || maxevents <= 0 {
|
if epfd_value <= 0 || maxevents <= 0 {
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
this.write_int(-1, dest)?;
|
this.write_int(-1, dest)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,7 @@ pub fn futex<'tcx>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if bitset == 0 {
|
if bitset == 0 {
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
|
this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -88,8 +87,7 @@ pub fn futex<'tcx>(
|
|||||||
let duration = match this.read_timespec(&timeout)? {
|
let duration = match this.read_timespec(&timeout)? {
|
||||||
Some(duration) => duration,
|
Some(duration) => duration,
|
||||||
None => {
|
None => {
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
|
this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -198,8 +196,7 @@ pub fn futex<'tcx>(
|
|||||||
u32::MAX
|
u32::MAX
|
||||||
};
|
};
|
||||||
if bitset == 0 {
|
if bitset == 0 {
|
||||||
let einval = this.eval_libc("EINVAL");
|
this.set_last_error(LibcError("EINVAL"))?;
|
||||||
this.set_last_error(einval)?;
|
|
||||||
this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
|
this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||||
this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?;
|
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));
|
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)?,
|
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))
|
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 {
|
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||||
this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?;
|
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"));
|
return Ok(this.eval_windows("c", "FALSE"));
|
||||||
}
|
}
|
||||||
@ -190,7 +190,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
match env::set_current_dir(path) {
|
match env::set_current_dir(path) {
|
||||||
Ok(()) => Ok(this.eval_windows("c", "TRUE")),
|
Ok(()) => Ok(this.eval_windows("c", "TRUE")),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
this.set_last_error_from_io_error(e)?;
|
this.set_last_error(e)?;
|
||||||
Ok(this.eval_windows("c", "FALSE"))
|
Ok(this.eval_windows("c", "FALSE"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,7 +227,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let filename = this.read_path_from_wide_str(filename)?;
|
let filename = this.read_path_from_wide_str(filename)?;
|
||||||
let result = match win_absolute(&filename)? {
|
let result = match win_absolute(&filename)? {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
this.set_last_error_from_io_error(err)?;
|
this.set_last_error(err)?;
|
||||||
Scalar::from_u32(0) // return zero upon failure
|
Scalar::from_u32(0) // return zero upon failure
|
||||||
}
|
}
|
||||||
Ok(abs_filename) => {
|
Ok(abs_filename) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user