Skip to content

Commit

Permalink
expose host-to-target path conversion to interpreted program
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Dec 12, 2022
1 parent 0805372 commit d267f7f
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 39 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,21 @@ extern "Rust" {

/// Miri-provided extern function to deallocate memory.
fn miri_dealloc(ptr: *mut u8, size: usize, align: usize);

/// Convert a path from the host Miri runs on to the target Miri interprets.
/// Performs conversion of path separators as needed.
///
/// Usually Miri performs this kind of conversion automatically. However, manual conversion
/// might be necessary when reading an environment variable that was set of the host
/// (such as TMPDIR) and using it as a target path.
///
/// Only works with isolation disabled.
///
/// `in` must point to a null-terminated string, and will be read as the input host path.
/// `out` must point to at least `out_size` many bytes, and the result will be stored there
/// with a null terminator.
/// Returns 0 if the `out` buffer was large enough, and the required size otherwise.
fn miri_host_to_target_path(path: *const i8, out: *mut i8, out_size: usize) -> usize;
}
```

Expand Down
17 changes: 16 additions & 1 deletion src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::hash_map::Entry, io::Write, iter};
use std::{collections::hash_map::Entry, io::Write, iter, path::Path};

use log::trace;

Expand Down Expand Up @@ -442,6 +442,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
this.machine.static_roots.push(alloc_id);
}
"miri_host_to_target_path" => {
let [ptr, out, out_size] = this.check_shim(abi, Abi::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let out = this.read_pointer(out)?;
let out_size = this.read_scalar(out_size)?.to_machine_usize(this)?;

// The host affects program behavior here, so this requires isolation to be disabled.
this.check_no_isolation("`miri_host_to_target_path`")?;

// We read this as a plain OsStr and write it as a path, which will convert it to the target.
let path = this.read_os_str_from_c_str(ptr)?.to_owned();
let (success, needed_size) = this.write_path_to_c_str(Path::new(&path), out, out_size)?;
// Return value: 0 on success, otherwise the size it would have needed.
this.write_int(if success { 0 } else { needed_size }, dest)?;
}

// Obtains the size of a Miri backtrace. See the README for details.
"miri_backtrace_size" => {
Expand Down
31 changes: 16 additions & 15 deletions tests/pass-dep/shims/libc-fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#![feature(io_error_uncategorized)]

use std::convert::TryInto;
use std::ffi::CString;
use std::ffi::{CStr, CString};
use std::fs::{canonicalize, remove_dir_all, remove_file, File};
use std::io::{Error, ErrorKind, Write};
use std::os::unix::ffi::OsStrExt;
Expand All @@ -23,20 +23,21 @@ fn main() {
}

fn tmp() -> PathBuf {
std::env::var("MIRI_TEMP")
.map(|tmp| {
// MIRI_TEMP is set outside of our emulated
// program, so it may have path separators that don't
// correspond to our target platform. We normalize them here
// before constructing a `PathBuf`

#[cfg(windows)]
return PathBuf::from(tmp.replace("/", "\\"));

#[cfg(not(windows))]
return PathBuf::from(tmp.replace("\\", "/"));
})
.unwrap_or_else(|_| std::env::temp_dir())
let path = std::env::var("MIRI_TEMP")
.unwrap_or_else(|_| std::env::temp_dir().into_os_string().into_string().unwrap());
// These are host paths. We need to convert them to the target.
let path = CString::new(path).unwrap();
let mut out = Vec::with_capacity(1024);

unsafe {
extern "Rust" {
fn miri_host_to_target_path(path: *const i8, out: *mut i8, out_size: usize) -> usize;
}
let ret = miri_host_to_target_path(path.as_ptr(), out.as_mut_ptr(), out.capacity());
assert_eq!(ret, 0);
let out = CStr::from_ptr(out.as_ptr()).to_str().unwrap();
PathBuf::from(out)
}
}

/// Prepare: compute filename and make sure the file does not exist.
Expand Down
26 changes: 17 additions & 9 deletions tests/pass-dep/shims/libc-misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@ use std::os::unix::io::AsRawFd;
use std::path::PathBuf;

fn tmp() -> PathBuf {
std::env::var("MIRI_TEMP")
.map(|tmp| {
// MIRI_TEMP is set outside of our emulated
// program, so it may have path separators that don't
// correspond to our target platform. We normalize them here
// before constructing a `PathBuf`
return PathBuf::from(tmp.replace("\\", "/"));
})
.unwrap_or_else(|_| std::env::temp_dir())
use std::ffi::{CStr, CString};

let path = std::env::var("MIRI_TEMP")
.unwrap_or_else(|_| std::env::temp_dir().into_os_string().into_string().unwrap());
// These are host paths. We need to convert them to the target.
let path = CString::new(path).unwrap();
let mut out = Vec::with_capacity(1024);

unsafe {
extern "Rust" {
fn miri_host_to_target_path(path: *const i8, out: *mut i8, out_size: usize) -> usize;
}
let ret = miri_host_to_target_path(path.as_ptr(), out.as_mut_ptr(), out.capacity());
assert_eq!(ret, 0);
let out = CStr::from_ptr(out.as_ptr()).to_str().unwrap();
PathBuf::from(out)
}
}

/// Test allocating variant of `realpath`.
Expand Down
31 changes: 17 additions & 14 deletions tests/pass/shims/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,23 @@ fn main() {
}

fn tmp() -> PathBuf {
std::env::var("MIRI_TEMP")
.map(|tmp| {
// MIRI_TEMP is set outside of our emulated
// program, so it may have path separators that don't
// correspond to our target platform. We normalize them here
// before constructing a `PathBuf`

#[cfg(windows)]
return PathBuf::from(tmp.replace("/", "\\"));

#[cfg(not(windows))]
return PathBuf::from(tmp.replace("\\", "/"));
})
.unwrap_or_else(|_| std::env::temp_dir())
use std::ffi::{CStr, CString};

let path = std::env::var("MIRI_TEMP")
.unwrap_or_else(|_| std::env::temp_dir().into_os_string().into_string().unwrap());
// These are host paths. We need to convert them to the target.
let path = CString::new(path).unwrap();
let mut out = Vec::with_capacity(1024);

unsafe {
extern "Rust" {
fn miri_host_to_target_path(path: *const i8, out: *mut i8, out_size: usize) -> usize;
}
let ret = miri_host_to_target_path(path.as_ptr(), out.as_mut_ptr(), out.capacity());
assert_eq!(ret, 0);
let out = CStr::from_ptr(out.as_ptr()).to_str().unwrap();
PathBuf::from(out)
}
}

/// Prepare: compute filename and make sure the file does not exist.
Expand Down

0 comments on commit d267f7f

Please sign in to comment.