Skip to content
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
6 changes: 4 additions & 2 deletions src/shims/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// `buf_size` represents the size in characters.
let buf_size = u64::from(this.read_scalar(size_op)?.to_u32()?);
Scalar::from_u32(windows_check_buffer_size(
this.write_os_str_to_wide_str(&var, buf_ptr, buf_size)?,
this.write_os_str_to_wide_str(
&var, buf_ptr, buf_size, /*truncate*/ false,
)?,
))
}
None => {
Expand Down Expand Up @@ -366,7 +368,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
match env::current_dir() {
Ok(cwd) =>
return Ok(Scalar::from_u32(windows_check_buffer_size(
this.write_path_to_wide_str(&cwd, buf, size)?,
this.write_path_to_wide_str(&cwd, buf, size, /*truncate*/ false)?,
))),
Err(e) => this.set_last_error_from_io_error(e.kind())?,
}
Expand Down
51 changes: 41 additions & 10 deletions src/shims/os_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
self.eval_context_mut().write_c_str(bytes, ptr, size)
}

/// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what
/// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying
/// to write if `size` is not large enough to fit the contents of `os_string` plus a null
/// terminator. It returns `Ok((true, length))` if the writing process was successful. The
/// string length returned does include the null terminator. Length is measured in units of
/// `u16.`
/// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what the
/// Windows APIs usually handle.
///
/// If `truncate == false` (the usual mode of operation), this function returns `Ok((false,
/// length))` without trying to write if `size` is not large enough to fit the contents of
/// `os_string` plus a null terminator. It returns `Ok((true, length))` if the writing process
/// was successful. The string length returned does include the null terminator. Length is
/// measured in units of `u16.`
///
/// If `truncate == true`, then in case `size` is not large enough it *will* write the first
/// `size.saturating_sub(1)` many items, followed by a null terminator (if `size > 0`).
fn write_os_str_to_wide_str(
&mut self,
os_str: &OsStr,
ptr: Pointer<Option<Provenance>>,
size: u64,
truncate: bool,
) -> InterpResult<'tcx, (bool, u64)> {
#[cfg(windows)]
fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
Expand All @@ -129,7 +135,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}

let u16_vec = os_str_to_u16vec(os_str)?;
self.eval_context_mut().write_wide_str(&u16_vec, ptr, size)
let (written, size_needed) = self.eval_context_mut().write_wide_str(&u16_vec, ptr, size)?;
if truncate && !written && size > 0 {
// Write the truncated part that fits.
let truncated_data = &u16_vec[..size.saturating_sub(1).try_into().unwrap()];
let (written, written_len) =
self.eval_context_mut().write_wide_str(truncated_data, ptr, size)?;
assert!(written && written_len == size);
}
Ok((written, size_needed))
}

/// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes.
Expand All @@ -143,7 +157,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

let arg_type = this.tcx.mk_array(this.tcx.types.u8, size);
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
assert!(self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap().0);
let (written, _) = self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap();
assert!(written);
Ok(arg_place.ptr)
}

Expand All @@ -158,7 +173,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

let arg_type = this.tcx.mk_array(this.tcx.types.u16, size);
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
assert!(self.write_os_str_to_wide_str(os_str, arg_place.ptr, size).unwrap().0);
let (written, _) =
self.write_os_str_to_wide_str(os_str, arg_place.ptr, size, /*truncate*/ false).unwrap();
assert!(written);
Ok(arg_place.ptr)
}

Expand Down Expand Up @@ -212,11 +229,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
path: &Path,
ptr: Pointer<Option<Provenance>>,
size: u64,
truncate: bool,
) -> InterpResult<'tcx, (bool, u64)> {
let this = self.eval_context_mut();
let os_str =
this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
this.write_os_str_to_wide_str(&os_str, ptr, size)
this.write_os_str_to_wide_str(&os_str, ptr, size, truncate)
}

/// Allocate enough memory to store a Path as a null-terminated sequence of bytes,
Expand All @@ -232,6 +250,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.alloc_os_str_as_c_str(&os_str, memkind)
}

/// Allocate enough memory to store a Path as a null-terminated sequence of `u16`s,
/// adjusting path separators if needed.
fn alloc_path_as_wide_str(
&mut self,
path: &Path,
memkind: MemoryKind<MiriMemoryKind>,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
let this = self.eval_context_mut();
let os_str =
this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
this.alloc_os_str_as_wide_str(&os_str, memkind)
}

#[allow(clippy::get_first)]
fn convert_path<'a>(
&self,
Expand Down
40 changes: 40 additions & 0 deletions src/shims/windows/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,46 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

this.write_scalar(Scalar::from_u32(1), dest)?;
}
"GetModuleFileNameW" => {
let [handle, filename, size] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.check_no_isolation("`GetModuleFileNameW`")?;

let handle = this.read_machine_usize(handle)?;
let filename = this.read_pointer(filename)?;
let size = this.read_scalar(size)?.to_u32()?;

if handle != 0 {
throw_unsup_format!("`GetModuleFileNameW` only supports the NULL handle");
}

// Using the host current_exe is a bit off, but consistent with Linux
// (where stdlib reads /proc/self/exe).
// Unfortunately this Windows function has a crazy behavior so we can't just use
// `write_path_to_wide_str`...
let path = std::env::current_exe().unwrap();
let (all_written, size_needed) = this.write_path_to_wide_str(
&path,
filename,
size.into(),
/*truncate*/ true,
)?;

if all_written {
// If the function succeeds, the return value is the length of the string that
// is copied to the buffer, in characters, not including the terminating null
// character.
this.write_int(size_needed.checked_sub(1).unwrap(), dest)?;
} else {
// If the buffer is too small to hold the module name, the string is truncated
// to nSize characters including the terminating null character, the function
// returns nSize, and the function sets the last error to
// ERROR_INSUFFICIENT_BUFFER.
this.write_int(size, dest)?;
let insufficient_buffer = this.eval_windows("c", "ERROR_INSUFFICIENT_BUFFER");
this.set_last_error(insufficient_buffer)?;
}
}

// Threading
"CreateThread" => {
Expand Down
1 change: 0 additions & 1 deletion tests/pass/shims/env/current_exe.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//@ignore-target-windows: current_exe not supported on Windows
//@only-on-host: the Linux std implementation opens /proc/self/exe, which doesn't work cross-target
//@compile-flags: -Zmiri-disable-isolation
use std::env;
Expand Down