// `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 => {
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())?,
}
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>> {
}
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.
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)
}
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)
}
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,
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,
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" => {