Auto merge of #3502 - RalfJung:GetUserProfileDirectoryW, r=RalfJung
windows: basic support for GetUserProfileDirectoryW Fixes https://github.com/rust-lang/miri/issues/3499
This commit is contained in:
commit
6c34d4605e
@ -299,6 +299,27 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "directories"
|
||||||
|
version = "5.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"option-ext",
|
||||||
|
"redox_users",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encode_unicode"
|
name = "encode_unicode"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
@ -490,6 +511,16 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libredox"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.4.2",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.13"
|
version = "0.4.13"
|
||||||
@ -558,6 +589,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"colored",
|
"colored",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
|
"directories",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"jemalloc-sys",
|
"jemalloc-sys",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@ -614,6 +646,12 @@ version = "1.19.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "option-ext"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owo-colors"
|
name = "owo-colors"
|
||||||
version = "3.5.0"
|
version = "3.5.0"
|
||||||
@ -746,6 +784,17 @@ dependencies = [
|
|||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"libredox",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.3"
|
version = "1.10.3"
|
||||||
|
@ -25,6 +25,7 @@ aes = { version = "0.8.3", features = ["hazmat"] }
|
|||||||
measureme = "11"
|
measureme = "11"
|
||||||
ctrlc = "3.2.5"
|
ctrlc = "3.2.5"
|
||||||
chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
|
chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
|
||||||
|
directories = "5"
|
||||||
|
|
||||||
# Copied from `compiler/rustc/Cargo.toml`.
|
# Copied from `compiler/rustc/Cargo.toml`.
|
||||||
# But only for some targets, it fails for others. Rustc configures this in its CI, but we can't
|
# But only for some targets, it fails for others. Rustc configures this in its CI, but we can't
|
||||||
|
@ -160,10 +160,12 @@ fn GetEnvironmentVariableW(
|
|||||||
this.assert_target_os("windows", "GetEnvironmentVariableW");
|
this.assert_target_os("windows", "GetEnvironmentVariableW");
|
||||||
|
|
||||||
let name_ptr = this.read_pointer(name_op)?;
|
let name_ptr = this.read_pointer(name_op)?;
|
||||||
|
let buf_ptr = this.read_pointer(buf_op)?;
|
||||||
|
let buf_size = this.read_scalar(size_op)?.to_u32()?; // in characters
|
||||||
|
|
||||||
let name = this.read_os_str_from_wide_str(name_ptr)?;
|
let name = this.read_os_str_from_wide_str(name_ptr)?;
|
||||||
Ok(match this.machine.env_vars.map.get(&name) {
|
Ok(match this.machine.env_vars.map.get(&name) {
|
||||||
Some(&var_ptr) => {
|
Some(&var_ptr) => {
|
||||||
this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error
|
|
||||||
// The offset is used to strip the "{name}=" part of the string.
|
// The offset is used to strip the "{name}=" part of the string.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let name_offset_bytes = u64::try_from(name.len()).unwrap()
|
let name_offset_bytes = u64::try_from(name.len()).unwrap()
|
||||||
@ -172,14 +174,13 @@ fn GetEnvironmentVariableW(
|
|||||||
let var_ptr = var_ptr.offset(Size::from_bytes(name_offset_bytes), this)?;
|
let var_ptr = var_ptr.offset(Size::from_bytes(name_offset_bytes), this)?;
|
||||||
let var = this.read_os_str_from_wide_str(var_ptr)?;
|
let var = this.read_os_str_from_wide_str(var_ptr)?;
|
||||||
|
|
||||||
let buf_ptr = this.read_pointer(buf_op)?;
|
Scalar::from_u32(windows_check_buffer_size(this.write_os_str_to_wide_str(
|
||||||
// `buf_size` represents the size in characters.
|
&var,
|
||||||
let buf_size = u64::from(this.read_scalar(size_op)?.to_u32()?);
|
buf_ptr,
|
||||||
Scalar::from_u32(windows_check_buffer_size(
|
buf_size.into(),
|
||||||
this.write_os_str_to_wide_str(
|
)?))
|
||||||
&var, buf_ptr, buf_size, /*truncate*/ false,
|
// This can in fact return 0. It is up to the caller to set last_error to 0
|
||||||
)?,
|
// beforehand and check it afterwards to exclude that case.
|
||||||
))
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let envvar_not_found = this.eval_windows("c", "ERROR_ENVVAR_NOT_FOUND");
|
let envvar_not_found = this.eval_windows("c", "ERROR_ENVVAR_NOT_FOUND");
|
||||||
@ -375,9 +376,10 @@ fn GetCurrentDirectoryW(
|
|||||||
// If we cannot get the current directory, we return 0
|
// If we cannot get the current directory, we return 0
|
||||||
match env::current_dir() {
|
match env::current_dir() {
|
||||||
Ok(cwd) => {
|
Ok(cwd) => {
|
||||||
this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error
|
// This can in fact return 0. It is up to the caller to set last_error to 0
|
||||||
|
// beforehand and check it afterwards to exclude that case.
|
||||||
return Ok(Scalar::from_u32(windows_check_buffer_size(
|
return Ok(Scalar::from_u32(windows_check_buffer_size(
|
||||||
this.write_path_to_wide_str(&cwd, buf, size, /*truncate*/ false)?,
|
this.write_path_to_wide_str(&cwd, buf, size)?,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
Err(e) => this.set_last_error_from_io_error(e.kind())?,
|
Err(e) => this.set_last_error_from_io_error(e.kind())?,
|
||||||
@ -494,9 +496,60 @@ fn getpid(&mut self) -> InterpResult<'tcx, i32> {
|
|||||||
fn GetCurrentProcessId(&mut self) -> InterpResult<'tcx, u32> {
|
fn GetCurrentProcessId(&mut self) -> InterpResult<'tcx, u32> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
this.assert_target_os("windows", "GetCurrentProcessId");
|
this.assert_target_os("windows", "GetCurrentProcessId");
|
||||||
|
|
||||||
this.check_no_isolation("`GetCurrentProcessId`")?;
|
this.check_no_isolation("`GetCurrentProcessId`")?;
|
||||||
|
|
||||||
Ok(std::process::id())
|
Ok(std::process::id())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn GetUserProfileDirectoryW(
|
||||||
|
&mut self,
|
||||||
|
token: &OpTy<'tcx, Provenance>, // HANDLE
|
||||||
|
buf: &OpTy<'tcx, Provenance>, // LPWSTR
|
||||||
|
size: &OpTy<'tcx, Provenance>, // LPDWORD
|
||||||
|
) -> InterpResult<'tcx, Scalar<Provenance>> // returns BOOL
|
||||||
|
{
|
||||||
|
let this = self.eval_context_mut();
|
||||||
|
this.assert_target_os("windows", "GetUserProfileDirectoryW");
|
||||||
|
this.check_no_isolation("`GetUserProfileDirectoryW`")?;
|
||||||
|
|
||||||
|
let token = this.read_target_isize(token)?;
|
||||||
|
let buf = this.read_pointer(buf)?;
|
||||||
|
let size = this.deref_pointer(size)?;
|
||||||
|
|
||||||
|
if token != -4 {
|
||||||
|
throw_unsup_format!(
|
||||||
|
"GetUserProfileDirectoryW: only CURRENT_PROCESS_TOKEN is supported"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// See <https://learn.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-getuserprofiledirectoryw> for docs.
|
||||||
|
Ok(match directories::UserDirs::new() {
|
||||||
|
Some(dirs) => {
|
||||||
|
let home = dirs.home_dir();
|
||||||
|
let size_avail = if this.ptr_is_null(size.ptr())? {
|
||||||
|
0 // if the buf pointer is null, we can't write to it; `size` will be updated to the required length
|
||||||
|
} else {
|
||||||
|
this.read_scalar(&size)?.to_u32()?
|
||||||
|
};
|
||||||
|
// Of course we cannot use `windows_check_buffer_size` here since this uses
|
||||||
|
// a different method for dealing with a too-small buffer than the other functions...
|
||||||
|
let (success, len) = this.write_path_to_wide_str(home, buf, size_avail.into())?;
|
||||||
|
// The Windows docs just say that this is written on failure. But std
|
||||||
|
// seems to rely on it always being written.
|
||||||
|
this.write_scalar(Scalar::from_u32(len.try_into().unwrap()), &size)?;
|
||||||
|
if success {
|
||||||
|
Scalar::from_i32(1) // return TRUE
|
||||||
|
} else {
|
||||||
|
this.set_last_error(this.eval_windows("c", "ERROR_INSUFFICIENT_BUFFER"))?;
|
||||||
|
Scalar::from_i32(0) // return FALSE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// We have to pick some error code.
|
||||||
|
this.set_last_error(this.eval_windows("c", "ERROR_BAD_USER_PROFILE"))?;
|
||||||
|
Scalar::from_i32(0) // return FALSE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,11 +72,9 @@ pub fn u16vec_to_osstring<'tcx>(u16_vec: Vec<u16>) -> InterpResult<'tcx, OsStrin
|
|||||||
u16vec_to_osstring(u16_vec)
|
u16vec_to_osstring(u16_vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what
|
/// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what the
|
||||||
/// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
|
/// Unix APIs usually handle. Returns `(success, full_len)`, where length includes the null
|
||||||
/// to write if `size` is not large enough to fit the contents of `os_string` plus a null
|
/// terminator. On failure, nothing is written.
|
||||||
/// terminator. It returns `Ok((true, length))` if the writing process was successful. The
|
|
||||||
/// string length returned does include the null terminator.
|
|
||||||
fn write_os_str_to_c_str(
|
fn write_os_str_to_c_str(
|
||||||
&mut self,
|
&mut self,
|
||||||
os_str: &OsStr,
|
os_str: &OsStr,
|
||||||
@ -87,19 +85,9 @@ fn write_os_str_to_c_str(
|
|||||||
self.eval_context_mut().write_c_str(bytes, ptr, size)
|
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
|
/// Internal helper to share code between `write_os_str_to_wide_str` and
|
||||||
/// Windows APIs usually handle.
|
/// `write_os_str_to_wide_str_truncated`.
|
||||||
///
|
fn write_os_str_to_wide_str_helper(
|
||||||
/// 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`).
|
|
||||||
/// The return value is still `(false, length)` in that case.
|
|
||||||
fn write_os_str_to_wide_str(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
os_str: &OsStr,
|
os_str: &OsStr,
|
||||||
ptr: Pointer<Option<Provenance>>,
|
ptr: Pointer<Option<Provenance>>,
|
||||||
@ -133,6 +121,29 @@ fn os_str_to_u16vec<'tcx>(os_str: &OsStr) -> InterpResult<'tcx, Vec<u16>> {
|
|||||||
Ok((written, size_needed))
|
Ok((written, size_needed))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what the
|
||||||
|
/// Windows APIs usually handle. Returns `(success, full_len)`, where length is measured
|
||||||
|
/// in units of `u16` and includes the null terminator. On failure, nothing is written.
|
||||||
|
fn write_os_str_to_wide_str(
|
||||||
|
&mut self,
|
||||||
|
os_str: &OsStr,
|
||||||
|
ptr: Pointer<Option<Provenance>>,
|
||||||
|
size: u64,
|
||||||
|
) -> InterpResult<'tcx, (bool, u64)> {
|
||||||
|
self.write_os_str_to_wide_str_helper(os_str, ptr, size, /*truncate*/ false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like `write_os_str_to_wide_str`, but on failure as much as possible is written into
|
||||||
|
/// the buffer (always with a null terminator).
|
||||||
|
fn write_os_str_to_wide_str_truncated(
|
||||||
|
&mut self,
|
||||||
|
os_str: &OsStr,
|
||||||
|
ptr: Pointer<Option<Provenance>>,
|
||||||
|
size: u64,
|
||||||
|
) -> InterpResult<'tcx, (bool, u64)> {
|
||||||
|
self.write_os_str_to_wide_str_helper(os_str, ptr, size, /*truncate*/ true)
|
||||||
|
}
|
||||||
|
|
||||||
/// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes.
|
/// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes.
|
||||||
fn alloc_os_str_as_c_str(
|
fn alloc_os_str_as_c_str(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -160,9 +171,7 @@ fn alloc_os_str_as_wide_str(
|
|||||||
|
|
||||||
let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u16, size);
|
let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u16, size);
|
||||||
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
|
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
|
||||||
let (written, _) = self
|
let (written, _) = self.write_os_str_to_wide_str(os_str, arg_place.ptr(), size).unwrap();
|
||||||
.write_os_str_to_wide_str(os_str, arg_place.ptr(), size, /*truncate*/ false)
|
|
||||||
.unwrap();
|
|
||||||
assert!(written);
|
assert!(written);
|
||||||
Ok(arg_place.ptr())
|
Ok(arg_place.ptr())
|
||||||
}
|
}
|
||||||
@ -217,12 +226,25 @@ fn write_path_to_wide_str(
|
|||||||
path: &Path,
|
path: &Path,
|
||||||
ptr: Pointer<Option<Provenance>>,
|
ptr: Pointer<Option<Provenance>>,
|
||||||
size: u64,
|
size: u64,
|
||||||
truncate: bool,
|
|
||||||
) -> InterpResult<'tcx, (bool, u64)> {
|
) -> InterpResult<'tcx, (bool, u64)> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
let os_str =
|
let os_str =
|
||||||
this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
|
this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
|
||||||
this.write_os_str_to_wide_str(&os_str, ptr, size, truncate)
|
this.write_os_str_to_wide_str(&os_str, ptr, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a Path to the machine memory (as a null-terminated sequence of `u16`s),
|
||||||
|
/// adjusting path separators if needed.
|
||||||
|
fn write_path_to_wide_str_truncated(
|
||||||
|
&mut self,
|
||||||
|
path: &Path,
|
||||||
|
ptr: Pointer<Option<Provenance>>,
|
||||||
|
size: u64,
|
||||||
|
) -> 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_truncated(&os_str, ptr, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate enough memory to store a Path as a null-terminated sequence of bytes,
|
/// Allocate enough memory to store a Path as a null-terminated sequence of bytes,
|
||||||
|
@ -23,7 +23,7 @@ fn mremap(
|
|||||||
// old_address must be a multiple of the page size
|
// old_address must be a multiple of the page size
|
||||||
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
|
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
|
||||||
if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 {
|
if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 {
|
||||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
|
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||||
return Ok(this.eval_libc("MAP_FAILED"));
|
return Ok(this.eval_libc("MAP_FAILED"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ fn mremap(
|
|||||||
|
|
||||||
if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 {
|
if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 {
|
||||||
// We only support MREMAP_MAYMOVE, so not passing the flag is just a failure
|
// We only support MREMAP_MAYMOVE, so not passing the flag is just a failure
|
||||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
|
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||||
return Ok(this.eval_libc("MAP_FAILED"));
|
return Ok(this.eval_libc("MAP_FAILED"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,11 +53,11 @@ fn mmap(
|
|||||||
|
|
||||||
// First, we do some basic argument validation as required by mmap
|
// First, we do some basic argument validation as required by mmap
|
||||||
if (flags & (map_private | map_shared)).count_ones() != 1 {
|
if (flags & (map_private | map_shared)).count_ones() != 1 {
|
||||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
|
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||||
return Ok(this.eval_libc("MAP_FAILED"));
|
return Ok(this.eval_libc("MAP_FAILED"));
|
||||||
}
|
}
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
|
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||||
return Ok(this.eval_libc("MAP_FAILED"));
|
return Ok(this.eval_libc("MAP_FAILED"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ fn mmap(
|
|||||||
//
|
//
|
||||||
// Miri doesn't support MAP_FIXED or any any protections other than PROT_READ|PROT_WRITE.
|
// Miri doesn't support MAP_FIXED or any any protections other than PROT_READ|PROT_WRITE.
|
||||||
if flags & map_fixed != 0 || prot != prot_read | prot_write {
|
if flags & map_fixed != 0 || prot != prot_read | prot_write {
|
||||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("ENOTSUP")))?;
|
this.set_last_error(this.eval_libc("ENOTSUP"))?;
|
||||||
return Ok(this.eval_libc("MAP_FAILED"));
|
return Ok(this.eval_libc("MAP_FAILED"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,11 +96,11 @@ fn mmap(
|
|||||||
|
|
||||||
let align = this.machine.page_align();
|
let align = this.machine.page_align();
|
||||||
let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else {
|
let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else {
|
||||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
|
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||||
return Ok(this.eval_libc("MAP_FAILED"));
|
return Ok(this.eval_libc("MAP_FAILED"));
|
||||||
};
|
};
|
||||||
if map_length > this.target_usize_max() {
|
if map_length > this.target_usize_max() {
|
||||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
|
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||||
return Ok(this.eval_libc("MAP_FAILED"));
|
return Ok(this.eval_libc("MAP_FAILED"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,16 +131,16 @@ fn munmap(
|
|||||||
// as a dealloc.
|
// as a dealloc.
|
||||||
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
|
#[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
|
||||||
if addr.addr().bytes() % this.machine.page_size != 0 {
|
if addr.addr().bytes() % this.machine.page_size != 0 {
|
||||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
|
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||||
return Ok(Scalar::from_i32(-1));
|
return Ok(Scalar::from_i32(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else {
|
let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else {
|
||||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
|
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||||
return Ok(Scalar::from_i32(-1));
|
return Ok(Scalar::from_i32(-1));
|
||||||
};
|
};
|
||||||
if length > this.target_usize_max() {
|
if length > this.target_usize_max() {
|
||||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
|
this.set_last_error(this.eval_libc("EINVAL"))?;
|
||||||
return Ok(this.eval_libc("MAP_FAILED"));
|
return Ok(this.eval_libc("MAP_FAILED"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +135,12 @@ fn emulate_foreign_item_inner(
|
|||||||
let result = this.SetCurrentDirectoryW(path)?;
|
let result = this.SetCurrentDirectoryW(path)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
|
"GetUserProfileDirectoryW" => {
|
||||||
|
let [token, buf, size] =
|
||||||
|
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
|
||||||
|
let result = this.GetUserProfileDirectoryW(token, buf, size)?;
|
||||||
|
this.write_scalar(result, dest)?;
|
||||||
|
}
|
||||||
|
|
||||||
// File related shims
|
// File related shims
|
||||||
"NtWriteFile" => {
|
"NtWriteFile" => {
|
||||||
@ -225,15 +231,11 @@ fn emulate_foreign_item_inner(
|
|||||||
Scalar::from_u32(0) // return zero upon failure
|
Scalar::from_u32(0) // return zero upon failure
|
||||||
}
|
}
|
||||||
Ok(abs_filename) => {
|
Ok(abs_filename) => {
|
||||||
this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error
|
|
||||||
Scalar::from_u32(helpers::windows_check_buffer_size(
|
Scalar::from_u32(helpers::windows_check_buffer_size(
|
||||||
this.write_path_to_wide_str(
|
this.write_path_to_wide_str(&abs_filename, buffer, size.into())?,
|
||||||
&abs_filename,
|
|
||||||
buffer,
|
|
||||||
size.into(),
|
|
||||||
/*truncate*/ false,
|
|
||||||
)?,
|
|
||||||
))
|
))
|
||||||
|
// This can in fact return 0. It is up to the caller to set last_error to 0
|
||||||
|
// beforehand and check it afterwards to exclude that case.
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
@ -601,15 +603,9 @@ fn emulate_foreign_item_inner(
|
|||||||
|
|
||||||
// Using the host current_exe is a bit off, but consistent with Linux
|
// Using the host current_exe is a bit off, but consistent with Linux
|
||||||
// (where stdlib reads /proc/self/exe).
|
// (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 path = std::env::current_exe().unwrap();
|
||||||
let (all_written, size_needed) = this.write_path_to_wide_str(
|
let (all_written, size_needed) =
|
||||||
&path,
|
this.write_path_to_wide_str_truncated(&path, filename, size.into())?;
|
||||||
filename,
|
|
||||||
size.into(),
|
|
||||||
/*truncate*/ true,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if all_written {
|
if all_written {
|
||||||
// If the function succeeds, the return value is the length of the string that
|
// If the function succeeds, the return value is the length of the string that
|
||||||
@ -649,12 +645,8 @@ fn emulate_foreign_item_inner(
|
|||||||
Some(err) => format!("{err}"),
|
Some(err) => format!("{err}"),
|
||||||
None => format!("<unknown error in FormatMessageW: {message_id}>"),
|
None => format!("<unknown error in FormatMessageW: {message_id}>"),
|
||||||
};
|
};
|
||||||
let (complete, length) = this.write_os_str_to_wide_str(
|
let (complete, length) =
|
||||||
OsStr::new(&formatted),
|
this.write_os_str_to_wide_str(OsStr::new(&formatted), buffer, size.into())?;
|
||||||
buffer,
|
|
||||||
size.into(),
|
|
||||||
/*truncate*/ false,
|
|
||||||
)?;
|
|
||||||
if !complete {
|
if !complete {
|
||||||
// The API docs don't say what happens when the buffer is not big enough...
|
// The API docs don't say what happens when the buffer is not big enough...
|
||||||
// Let's just bail.
|
// Let's just bail.
|
||||||
|
2
src/tools/miri/tests/pass/shims/env/home.rs
vendored
2
src/tools/miri/tests/pass/shims/env/home.rs
vendored
@ -1,9 +1,9 @@
|
|||||||
//@ignore-target-windows: home_dir is not supported on Windows
|
|
||||||
//@compile-flags: -Zmiri-disable-isolation
|
//@compile-flags: -Zmiri-disable-isolation
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env::remove_var("HOME"); // make sure we enter the interesting codepath
|
env::remove_var("HOME"); // make sure we enter the interesting codepath
|
||||||
|
env::remove_var("USERPROFILE"); // Windows also looks as this env var
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
env::home_dir().unwrap();
|
env::home_dir().unwrap();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user