Skip to content

Change the last OS error location to a place #991

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
64 changes: 64 additions & 0 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Tag>) -> 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<Tag>> {
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<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())
}
}
}
}
4 changes: 2 additions & 2 deletions src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ pub struct Evaluator<'tcx> {
pub(crate) argv: Option<Pointer<Tag>>,
pub(crate) cmd_line: Option<Pointer<Tag>>,

/// Last OS error.
pub(crate) last_error: Option<Pointer<Tag>>,
/// Last OS error location in memory. It is a 32-bit integer
pub(crate) last_error: Option<MPlaceTy<'tcx, Tag>>,

/// TLS state.
pub(crate) tls: TlsData<'tcx>,
Expand Down
4 changes: 2 additions & 2 deletions src/shims/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand All @@ -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)
}
}
Expand Down
32 changes: 2 additions & 30 deletions src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
}

"__errno_location" | "__error" => {
let errno_scalar: Scalar<Tag> = 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" => {
Expand Down Expand Up @@ -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<Tag>) -> 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<Tag>> {
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.
Expand Down
30 changes: 5 additions & 25 deletions src/shims/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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))
})
}

Expand Down Expand Up @@ -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))
})
}

Expand Down Expand Up @@ -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?)
})
}

Expand All @@ -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
Expand Down Expand Up @@ -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<T>` 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<i32>` instead of `i32` directly because some IO related
/// functions return different integer types (like `read`, that returns an `i64`)
fn consume_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().consume_io_error(e)?;
Ok((-1).into())
}
}
}
}