diff --git a/README.md b/README.md index 909e5bd510..8cc7e84695 100644 --- a/README.md +++ b/README.md @@ -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; } ``` diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 8370e02b58..abfa73db64 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -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; @@ -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" => { diff --git a/tests/pass-dep/shims/libc-fs.rs b/tests/pass-dep/shims/libc-fs.rs index 93c0fad9c1..ba5b269f65 100644 --- a/tests/pass-dep/shims/libc-fs.rs +++ b/tests/pass-dep/shims/libc-fs.rs @@ -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; @@ -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. diff --git a/tests/pass-dep/shims/libc-misc.rs b/tests/pass-dep/shims/libc-misc.rs index 2a4300fcd0..20e96a92c7 100644 --- a/tests/pass-dep/shims/libc-misc.rs +++ b/tests/pass-dep/shims/libc-misc.rs @@ -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`. diff --git a/tests/pass/shims/fs.rs b/tests/pass/shims/fs.rs index 65cf6fe66b..901a2ab102 100644 --- a/tests/pass/shims/fs.rs +++ b/tests/pass/shims/fs.rs @@ -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.