diff --git a/src/eval.rs b/src/eval.rs index 6fb1cd25b15..f4a8d176172 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -183,8 +183,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( let errno_layout = ecx.layout_of(ecx.tcx.types.u32)?; let errno_place = ecx.allocate(errno_layout, MiriMemoryKind::Static.into()); ecx.write_scalar(Scalar::from_u32(0), errno_place.into())?; - let errno_ptr = ecx.check_mplace_access(errno_place.into(), Some(Size::from_bits(32)))?; - ecx.machine.last_error = errno_ptr; + ecx.machine.last_error = Some(errno_place); Ok(ecx) } diff --git a/src/helpers.rs b/src/helpers.rs index 0c22a0f2e06..16091bb242c 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -345,4 +345,68 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } Ok(()) } + + /// 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.machine.last_error.unwrap(); + this.write_scalar(scalar, errno_place.into()) + } + + /// Gets the last error variable. + fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + let errno_place = this.machine.last_error.unwrap(); + this.read_scalar(errno_place.into())?.not_undef() + } + + /// Sets the last OS error using a `std::io::Error`. This function tries to produce the most + /// similar OS error from the `std::io::ErrorKind` and sets it as the last OS error. + fn set_last_error_from_io_error(&mut self, e: std::io::Error) -> InterpResult<'tcx> { + use std::io::ErrorKind::*; + let this = self.eval_context_mut(); + let target = &this.tcx.tcx.sess.target.target; + let last_error = if target.options.target_family == Some("unix".to_owned()) { + this.eval_libc(match e.kind() { + ConnectionRefused => "ECONNREFUSED", + ConnectionReset => "ECONNRESET", + PermissionDenied => "EPERM", + BrokenPipe => "EPIPE", + NotConnected => "ENOTCONN", + ConnectionAborted => "ECONNABORTED", + AddrNotAvailable => "EADDRNOTAVAIL", + AddrInUse => "EADDRINUSE", + NotFound => "ENOENT", + Interrupted => "EINTR", + InvalidInput => "EINVAL", + TimedOut => "ETIMEDOUT", + AlreadyExists => "EEXIST", + WouldBlock => "EWOULDBLOCK", + _ => throw_unsup_format!("The {} error cannot be transformed into a raw os error", e) + })? + } else { + // FIXME: we have to implement the windows' equivalent of this. + throw_unsup_format!("Setting the last OS error from an io::Error is unsupported for {}.", target.target_os) + }; + this.set_last_error(last_error) + } + + /// 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/machine.rs b/src/machine.rs index 3878a086053..50f0ecf5909 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -91,8 +91,8 @@ pub struct Evaluator<'tcx> { pub(crate) argv: Option>, pub(crate) cmd_line: Option>, - /// Last OS error. - pub(crate) last_error: Option>, + /// Last OS error location in memory. It is a 32-bit integer + pub(crate) last_error: Option>, /// TLS state. pub(crate) tls: TlsData<'tcx>, diff --git a/src/shims/env.rs b/src/shims/env.rs index 6078ca26e26..661e8bf209b 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -146,7 +146,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let erange = this.eval_libc("ERANGE")?; this.set_last_error(erange)?; } - Err(e) => this.consume_io_error(e)?, + Err(e) => this.set_last_error_from_io_error(e)?, } Ok(Scalar::ptr_null(&*this.tcx)) } @@ -168,7 +168,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx match env::set_current_dir(path) { Ok(()) => Ok(0), Err(e) => { - this.consume_io_error(e)?; + this.set_last_error_from_io_error(e)?; Ok(-1) } } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index b28e361b137..1933aee1151 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -414,8 +414,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } "__errno_location" | "__error" => { - let errno_scalar: Scalar = this.machine.last_error.unwrap().into(); - this.write_scalar(errno_scalar, dest)?; + let errno_place = this.machine.last_error.unwrap(); + this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?; } "getenv" => { @@ -977,34 +977,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } return Ok(None); } - - fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - let errno_ptr = this.machine.last_error.unwrap(); - // We allocated this during machine initialziation so the bounds are fine. - this.memory.get_mut(errno_ptr.alloc_id)?.write_scalar( - &*this.tcx, - errno_ptr, - scalar.into(), - Size::from_bits(32), - ) - } - - fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_mut(); - let errno_ptr = this.machine.last_error.unwrap(); - this.memory - .get(errno_ptr.alloc_id)? - .read_scalar(&*this.tcx, errno_ptr, Size::from_bits(32))? - .not_undef() - } - - fn consume_io_error(&mut self, e: std::io::Error) -> InterpResult<'tcx> { - self.eval_context_mut().set_last_error(Scalar::from_int( - e.raw_os_error().unwrap(), - Size::from_bits(32), - )) - } } // Shims the linux 'getrandom()' syscall. diff --git a/src/shims/fs.rs b/src/shims/fs.rs index ed2465cd1f9..ffcfab10081 100644 --- a/src/shims/fs.rs +++ b/src/shims/fs.rs @@ -108,7 +108,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fh.low }); - this.consume_result(fd) + this.try_unwrap_io_result(fd) } fn fcntl( @@ -144,7 +144,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let fd = this.read_scalar(fd_op)?.to_i32()?; this.remove_handle_and(fd, |handle, this| { - this.consume_result(handle.file.sync_all().map(|_| 0i32)) + this.try_unwrap_io_result(handle.file.sync_all().map(|_| 0i32)) }) } @@ -175,9 +175,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx .get_bytes_mut(&*this.tcx, buf, Size::from_bytes(count)) .map(|buffer| handle.file.read(buffer)) }); - // Reinsert the file handle this.machine.file_handler.handles.insert(fd, handle).unwrap_none(); - this.consume_result(bytes?.map(|bytes| bytes as i64)) + this.try_unwrap_io_result(bytes?.map(|bytes| bytes as i64)) }) } @@ -206,7 +205,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx .map(|bytes| handle.file.write(bytes).map(|bytes| bytes as i64)) }); this.machine.file_handler.handles.insert(fd, handle).unwrap_none(); - this.consume_result(bytes?) + this.try_unwrap_io_result(bytes?) }) } @@ -223,7 +222,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let result = remove_file(path).map(|_| 0); - this.consume_result(result) + this.try_unwrap_io_result(result) } /// Helper function that gets a `FileHandle` immutable reference and allows to manipulate it @@ -271,23 +270,4 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok((-1).into()) } } - - /// Helper function that consumes an `std::io::Result` and returns an - /// `InterpResult<'tcx,T>::Ok` instead. It is expected that the result can be converted to an - /// OS error using `std::io::Error::raw_os_error`. - /// - /// 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 consume_result>( - &mut self, - result: std::io::Result, - ) -> InterpResult<'tcx, T> { - match result { - Ok(ok) => Ok(ok), - Err(e) => { - self.eval_context_mut().consume_io_error(e)?; - Ok((-1).into()) - } - } - } }