# Build once, for the user to see.
cargo run $CARGO_BUILD_FLAGS --bin cargo-miri -- miri setup "$@"
# Call again, to just set env var.
- eval $(cargo run $CARGO_BUILD_FLAGS -q --bin cargo-miri -- miri setup --env "$@")
+ export MIRI_SYSROOT="$(cargo run $CARGO_BUILD_FLAGS -q --bin cargo-miri -- miri setup --print-sysroot "$@")"
}
# Prepare and set MIRI_SYSROOT. Respects `MIRI_TEST_TARGET` and takes into account
-fa0f7d0080d8e7e9eb20aa9cbf8013f96c81287f
+7979016aff545f7b41cc517031026020b340989d
// First, we need xargo.
if xargo_version().map_or(true, |v| v < (0, 3, 16)) {
+ if std::env::var("XARGO").is_ok() {
+ // The user manually gave us a xargo binary; don't do anything automatically.
+ show_error(format!("Your xargo is too old; please upgrade to the latest version"))
+ }
let mut cmd = cargo();
cmd.args(&["install", "xargo", "-f"]);
ask_to_run(cmd, ask_user, "install a recent enough xargo");
File::create(dir.join("lib.rs")).unwrap();
// Prepare xargo invocation.
let target = get_arg_flag_value("--target");
- let print_env = !ask_user && has_arg_flag("--env"); // whether we just print the necessary environment variable
+ let print_sysroot = !ask_user && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
let mut command = xargo();
command.arg("build").arg("-q");
command.current_dir(&dir);
};
let sysroot = if is_host { dir.join("HOST") } else { PathBuf::from(dir) };
std::env::set_var("MIRI_SYSROOT", &sysroot); // pass the env var to the processes we spawn, which will turn it into "--sysroot" flags
- if print_env {
- // Escape an arbitrary string for the shell: by wrapping it in `'`, the only special
- // character we have to worry about is `'` itself. Everything else is taken literally
- // in these strings. `'` is encoded as `'"'"'`: the outer `'` end and being a
- // `'`-quoted string, respectively; the `"'"` in the middle represents a single `'`.
- // (We could use `'\''` instead of `'"'"'` if we wanted but let's avoid backslashes.)
- println!("MIRI_SYSROOT='{}'", sysroot.display().to_string().replace('\'', r#"'"'"'"#));
+ if print_sysroot {
+ // Print just the sysroot and nothing else; this way we do not need any escaping.
+ println!("{}", sysroot.display());
} else if !ask_user {
println!("A libstd for Miri is now available in `{}`.", sysroot.display());
}
MiriMemoryKind::Env.into(),
);
ecx.machine.cmd_line = Some(cmd_ptr);
- // Store the UTF-16 string.
+ // Store the UTF-16 string. We just allocated so we know the bounds are fine.
let char_size = Size::from_bytes(2);
let cmd_alloc = ecx.memory.get_mut(cmd_ptr.alloc_id)?;
let mut cur_ptr = cmd_ptr;
}
}
- assert!(
- args.next().is_none(),
- "start lang item has more arguments than expected"
- );
+ args.next().expect_none("start lang item has more arguments than expected");
// Set the last_error to 0
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)
}
}
let this = self.eval_context_mut();
+ // Don't forget the bounds check.
let ptr = this.memory.check_ptr_access(
ptr,
Size::from_bytes(len as u64),
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())
+ }
+ }
+ }
+
/// Helper function to read an OsString from a null-terminated sequence of bytes, which is what
/// the Unix APIs usually handle.
fn read_os_string_from_c_string(&mut self, scalar: Scalar<Tag>) -> InterpResult<'tcx, OsString> {
#![feature(rustc_private)]
+#![feature(option_expect_none, option_unwrap_none)]
#![warn(rust_2018_idioms)]
#![allow(clippy::cast_lossless)]
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>,
ecx.write_scalar(Scalar::from_uint(align, arg.layout.size), arg)?;
// No more arguments.
- assert!(
- args.next().is_none(),
- "`exchange_malloc` lang item has more arguments than expected"
- );
+ args.next().expect_none("`exchange_malloc` lang item has more arguments than expected");
Ok(())
}
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))
}
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)
}
}
.memory
.allocate(Size::from_bytes(size), align, kind.into());
if zero_init {
- // We just allocated this, the access cannot fail
+ // We just allocated this, the access is definitely in-bounds.
this.memory
.get_mut(ptr.alloc_id)
.unwrap()
Align::from_bytes(align).unwrap(),
MiriMemoryKind::Rust.into(),
);
- // We just allocated this, the access cannot fail
+ // We just allocated this, the access is definitely in-bounds.
this.memory
.get_mut(ptr.alloc_id)
.unwrap()
let arg_dest = this.local_place(arg_local)?;
this.write_scalar(data, arg_dest)?;
- assert!(
- args.next().is_none(),
- "__rust_maybe_catch_panic argument has more arguments than expected"
- );
+ args.next().expect_none("__rust_maybe_catch_panic argument has more arguments than expected");
// We ourselves will return `0`, eventually (because we will not return if we paniced).
this.write_null(dest)?;
}
"__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" => {
// Hook pthread calls that go to the thread-local storage memory subsystem.
"pthread_key_create" => {
- let key_ptr = this.read_scalar(args[0])?.not_undef()?;
+ let key_place = this.deref_operand(args[0])?;
// Extract the function type out of the signature (that seems easier than constructing it ourselves).
let dtor = match this.test_null(this.read_scalar(args[1])?.not_undef()?)? {
throw_unsup!(OutOfTls);
}
- let key_ptr = this
- .memory
- .check_ptr_access(key_ptr, key_layout.size, key_layout.align.abi)?
- .expect("cannot be a ZST");
- this.memory.get_mut(key_ptr.alloc_id)?.write_scalar(
- tcx,
- key_ptr,
- Scalar::from_uint(key, key_layout.size).into(),
- key_layout.size,
- )?;
+ this.write_scalar(Scalar::from_uint(key, key_layout.size), key_place.into())?;
// Return success (`0`).
this.write_null(dest)?;
let system_info_ptr = this
.check_mplace_access(system_info, None)?
.expect("cannot be a ZST");
+ // We rely on `deref_operand` doing bounds checks for us.
// Initialize with `0`.
this.memory
.get_mut(system_info_ptr.alloc_id)?
}
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();
- 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.
use crate::stacked_borrows::Tag;
use crate::*;
+#[derive(Debug)]
pub struct FileHandle {
file: File,
}
fn default() -> Self {
FileHandler {
handles: Default::default(),
- // 0, 1 and 2 are reserved for stdin, stdout and stderr
+ // 0, 1 and 2 are reserved for stdin, stdout and stderr.
low: 3,
}
}
let fd = options.open(path).map(|file| {
let mut fh = &mut this.machine.file_handler;
fh.low += 1;
- fh.handles.insert(fh.low, FileHandle { file });
+ fh.handles.insert(fh.low, FileHandle { file }).unwrap_none();
fh.low
});
- this.consume_result(fd)
+ this.try_unwrap_io_result(fd)
}
fn fcntl(
let fd = this.read_scalar(fd_op)?.to_i32()?;
let cmd = this.read_scalar(cmd_op)?.to_i32()?;
- // We only support getting the flags for a descriptor
+ // We only support getting the flags for a descriptor.
if cmd == this.eval_libc_i32("F_GETFD")? {
// Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
// `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
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))
})
}
this.check_no_isolation("read")?;
let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
- // Reading zero bytes should not change `buf`
+ // Reading zero bytes should not change `buf`.
if count == 0 {
return Ok(0);
}
let fd = this.read_scalar(fd_op)?.to_i32()?;
let buf_scalar = this.read_scalar(buf_op)?.not_undef()?;
- // Remove the file handle to avoid borrowing issues
+ // Remove the file handle to avoid borrowing issues.
this.remove_handle_and(fd, |mut handle, this| {
- // Don't use `?` to avoid returning before reinserting the handle
+ // Don't use `?` to avoid returning before reinserting the handle.
let bytes = this.force_ptr(buf_scalar).and_then(|buf| {
this.memory
.get_mut(buf.alloc_id)?
.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);
- this.consume_result(bytes?.map(|bytes| bytes as i64))
+ this.machine.file_handler.handles.insert(fd, handle).unwrap_none();
+ this.try_unwrap_io_result(bytes?.map(|bytes| bytes as i64))
})
}
this.check_no_isolation("write")?;
let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
- // Writing zero bytes should not change `buf`
+ // Writing zero bytes should not change `buf`.
if count == 0 {
return Ok(0);
}
.get_bytes(&*this.tcx, buf, Size::from_bytes(count))
.map(|bytes| handle.file.write(bytes).map(|bytes| bytes as i64))
});
- this.machine.file_handler.handles.insert(fd, handle);
- this.consume_result(bytes?)
+ this.machine.file_handler.handles.insert(fd, handle).unwrap_none();
+ this.try_unwrap_io_result(bytes?)
})
}
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
/// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
///
/// 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`)
+ /// functions return different integer types (like `read`, that returns an `i64`).
fn get_handle_and<F, T: From<i32>>(&mut self, fd: i32, f: F) -> InterpResult<'tcx, T>
where
F: Fn(&FileHandle) -> InterpResult<'tcx, T>,
/// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
///
/// 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`)
+ /// functions return different integer types (like `read`, that returns an `i64`).
fn remove_handle_and<F, T: From<i32>>(&mut self, fd: i32, mut f: F) -> InterpResult<'tcx, T>
where
F: FnMut(FileHandle, &mut MiriEvalContext<'mir, 'tcx>) -> InterpResult<'tcx, T>,
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())
- }
- }
- }
}
_ => {
// Do it in memory
let mplace = this.force_allocation(dest)?;
- assert!(mplace.meta.is_none());
+ mplace.meta.unwrap_none();
// not a zst, must be valid pointer
let ptr = mplace.ptr.to_ptr()?;
+ // we know the return place is in-bounds
this.memory.get_mut(ptr.alloc_id)?.write_repeat(tcx, ptr, 0, dest.layout.size)?;
}
}
_ => {
// Do it in memory
let mplace = this.force_allocation(dest)?;
- assert!(mplace.meta.is_none());
+ mplace.meta.unwrap_none();
let ptr = mplace.ptr.to_ptr()?;
+ // We know the return place is in-bounds
this.memory
.get_mut(ptr.alloc_id)?
.mark_definedness(ptr, dest.layout.size, false);
data: None,
dtor,
},
- );
+ ).unwrap_none();
trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor);
new_key
}
pub fn new_call(&mut self) -> CallId {
let id = self.next_call_id;
trace!("new_call: Assigning ID {}", id);
- self.active_calls.insert(id);
+ assert!(self.active_calls.insert(id));
self.next_call_id = NonZeroU64::new(id.get() + 1).unwrap();
id
}
self.base_ptr_ids.get(&id).copied().unwrap_or_else(|| {
let tag = Tag::Tagged(self.new_ptr());
trace!("New allocation {:?} has base tag {:?}", id, tag);
- self.base_ptr_ids.insert(id, tag);
+ self.base_ptr_ids.insert(id, tag).unwrap_none();
tag
})
}