Skip to content

Commit 0537dfa

Browse files
committed
expose host-to-target path conversion to interpreted program
1 parent 0805372 commit 0537dfa

File tree

5 files changed

+82
-38
lines changed

5 files changed

+82
-38
lines changed

README.md

+15
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,21 @@ extern "Rust" {
576576

577577
/// Miri-provided extern function to deallocate memory.
578578
fn miri_dealloc(ptr: *mut u8, size: usize, align: usize);
579+
580+
/// Convert a path from the host Miri runs on to the target Miri interprets.
581+
/// Performs conversion of path separators as needed.
582+
///
583+
/// Usually Miri performs this kind of conversion automatically. However, manual conversion
584+
/// might be necessary when reading an environment variable that was set of the host
585+
/// (such as TMPDIR) and using it as a target path.
586+
///
587+
/// Only works with isolation disabled.
588+
///
589+
/// `in` must point to a null-terminated string, and will be read as the input host path.
590+
/// `out` must point to at least `out_size` many bytes, and the result will be stored there
591+
/// with a null terminator.
592+
/// Returns 0 if the `out` buffer was large enough, and the required size otherwise.
593+
fn miri_host_to_target_path(path: *const i8, out: *mut i8, out_size: usize) -> usize;
579594
}
580595
```
581596

src/shims/foreign_items.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{collections::hash_map::Entry, io::Write, iter};
1+
use std::{collections::hash_map::Entry, io::Write, iter, path::Path};
22

33
use log::trace;
44

@@ -442,6 +442,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
442442
}
443443
this.machine.static_roots.push(alloc_id);
444444
}
445+
"miri_host_to_target_path" => {
446+
let [ptr, out, out_size] = this.check_shim(abi, Abi::Rust, link_name, args)?;
447+
let ptr = this.read_pointer(ptr)?;
448+
let out = this.read_pointer(out)?;
449+
let out_size = this.read_scalar(out_size)?.to_machine_usize(this)?;
450+
451+
// The host affects program behavior here, so this requires isolation to be disabled.
452+
this.check_no_isolation("`miri_host_to_target_path`")?;
453+
454+
// We read this as a plain OsStr, and write it as a path which will convert it to the target.
455+
let path = this.read_os_str_from_c_str(ptr)?.to_owned();
456+
let (success, needed_size) = this.write_path_to_c_str(Path::new(&path), out, out_size)?;
457+
// Return value: 0 on success, otherwise the size if would have needed.
458+
this.write_int(if success { 0 } else { needed_size }, dest)?;
459+
}
445460

446461
// Obtains the size of a Miri backtrace. See the README for details.
447462
"miri_backtrace_size" => {

tests/pass-dep/shims/libc-fs.rs

+17-14
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,23 @@ fn main() {
2323
}
2424

2525
fn tmp() -> PathBuf {
26-
std::env::var("MIRI_TEMP")
27-
.map(|tmp| {
28-
// MIRI_TEMP is set outside of our emulated
29-
// program, so it may have path separators that don't
30-
// correspond to our target platform. We normalize them here
31-
// before constructing a `PathBuf`
32-
33-
#[cfg(windows)]
34-
return PathBuf::from(tmp.replace("/", "\\"));
35-
36-
#[cfg(not(windows))]
37-
return PathBuf::from(tmp.replace("\\", "/"));
38-
})
39-
.unwrap_or_else(|_| std::env::temp_dir())
26+
use std::ffi::{CStr, CString};
27+
28+
let path = std::env::var("MIRI_TEMP")
29+
.unwrap_or_else(|_| std::env::temp_dir().into_os_string().into_string().unwrap());
30+
// These are host paths. We need to convert them to the target.
31+
let path = CString::new(path).unwrap();
32+
let mut out = Vec::with_capacity(1024);
33+
34+
unsafe {
35+
extern "Rust" {
36+
fn miri_host_to_target_path(path: *const i8, out: *mut i8, out_size: usize) -> usize;
37+
}
38+
let ret = miri_host_to_target_path(path.as_ptr(), out.as_mut_ptr(), out.capacity());
39+
assert_eq!(ret, 0);
40+
let out = CStr::from_ptr(out.as_ptr()).to_str().unwrap();
41+
PathBuf::from(out)
42+
}
4043
}
4144

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

tests/pass-dep/shims/libc-misc.rs

+17-9
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,23 @@ use std::os::unix::io::AsRawFd;
77
use std::path::PathBuf;
88

99
fn tmp() -> PathBuf {
10-
std::env::var("MIRI_TEMP")
11-
.map(|tmp| {
12-
// MIRI_TEMP is set outside of our emulated
13-
// program, so it may have path separators that don't
14-
// correspond to our target platform. We normalize them here
15-
// before constructing a `PathBuf`
16-
return PathBuf::from(tmp.replace("\\", "/"));
17-
})
18-
.unwrap_or_else(|_| std::env::temp_dir())
10+
use std::ffi::{CStr, CString};
11+
12+
let path = std::env::var("MIRI_TEMP")
13+
.unwrap_or_else(|_| std::env::temp_dir().into_os_string().into_string().unwrap());
14+
// These are host paths. We need to convert them to the target.
15+
let path = CString::new(path).unwrap();
16+
let mut out = Vec::with_capacity(1024);
17+
18+
unsafe {
19+
extern "Rust" {
20+
fn miri_host_to_target_path(path: *const i8, out: *mut i8, out_size: usize) -> usize;
21+
}
22+
let ret = miri_host_to_target_path(path.as_ptr(), out.as_mut_ptr(), out.capacity());
23+
assert_eq!(ret, 0);
24+
let out = CStr::from_ptr(out.as_ptr()).to_str().unwrap();
25+
PathBuf::from(out)
26+
}
1927
}
2028

2129
/// Test allocating variant of `realpath`.

tests/pass/shims/fs.rs

+17-14
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,23 @@ fn main() {
3131
}
3232

3333
fn tmp() -> PathBuf {
34-
std::env::var("MIRI_TEMP")
35-
.map(|tmp| {
36-
// MIRI_TEMP is set outside of our emulated
37-
// program, so it may have path separators that don't
38-
// correspond to our target platform. We normalize them here
39-
// before constructing a `PathBuf`
40-
41-
#[cfg(windows)]
42-
return PathBuf::from(tmp.replace("/", "\\"));
43-
44-
#[cfg(not(windows))]
45-
return PathBuf::from(tmp.replace("\\", "/"));
46-
})
47-
.unwrap_or_else(|_| std::env::temp_dir())
34+
use std::ffi::{CStr, CString};
35+
36+
let path = std::env::var("MIRI_TEMP")
37+
.unwrap_or_else(|_| std::env::temp_dir().into_os_string().into_string().unwrap());
38+
// These are host paths. We need to convert them to the target.
39+
let path = CString::new(path).unwrap();
40+
let mut out = Vec::with_capacity(1024);
41+
42+
unsafe {
43+
extern "Rust" {
44+
fn miri_host_to_target_path(path: *const i8, out: *mut i8, out_size: usize) -> usize;
45+
}
46+
let ret = miri_host_to_target_path(path.as_ptr(), out.as_mut_ptr(), out.capacity());
47+
assert_eq!(ret, 0);
48+
let out = CStr::from_ptr(out.as_ptr()).to_str().unwrap();
49+
PathBuf::from(out)
50+
}
4851
}
4952

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

0 commit comments

Comments
 (0)