diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index 402e2670888..298fefdb0f3 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -178,7 +178,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &var, buf_ptr, buf_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. @@ -380,7 +379,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // 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( - 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())?, @@ -535,12 +534,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; // 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(), - /*truncate*/ false, - )?; + 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)?; diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs index 3e8c35d48ae..5fcea9ced69 100644 --- a/src/tools/miri/src/shims/os_str.rs +++ b/src/tools/miri/src/shims/os_str.rs @@ -72,11 +72,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { u16vec_to_osstring(u16_vec) } - /// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what - /// the Unix 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. + /// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what the + /// Unix APIs usually handle. Returns `(success, full_len)`, where length includes the null + /// terminator. On failure, nothing is written. fn write_os_str_to_c_str( &mut self, os_str: &OsStr, @@ -87,19 +85,9 @@ 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. - /// - /// 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( + /// Internal helper to share code between `write_os_str_to_wide_str` and + /// `write_os_str_to_wide_str_truncated`. + fn write_os_str_to_wide_str_helper( &mut self, os_str: &OsStr, ptr: Pointer>, @@ -133,6 +121,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { 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>, + 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>, + 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. fn alloc_os_str_as_c_str( &mut self, @@ -160,9 +171,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { 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 (written, _) = self - .write_os_str_to_wide_str(os_str, arg_place.ptr(), size, /*truncate*/ false) - .unwrap(); + let (written, _) = self.write_os_str_to_wide_str(os_str, arg_place.ptr(), size).unwrap(); assert!(written); Ok(arg_place.ptr()) } @@ -217,12 +226,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { path: &Path, ptr: Pointer>, 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, 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>, + 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, diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index e0cca43d263..24f7cd18e7a 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -232,12 +232,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } Ok(abs_filename) => { Scalar::from_u32(helpers::windows_check_buffer_size( - this.write_path_to_wide_str( - &abs_filename, - buffer, - size.into(), - /*truncate*/ false, - )?, + this.write_path_to_wide_str(&abs_filename, buffer, size.into())?, )) // 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. @@ -608,15 +603,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // 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, - )?; + let (all_written, size_needed) = + this.write_path_to_wide_str_truncated(&path, filename, size.into())?; if all_written { // If the function succeeds, the return value is the length of the string that @@ -656,12 +645,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Some(err) => format!("{err}"), None => format!(""), }; - let (complete, length) = this.write_os_str_to_wide_str( - OsStr::new(&formatted), - buffer, - size.into(), - /*truncate*/ false, - )?; + let (complete, length) = + this.write_os_str_to_wide_str(OsStr::new(&formatted), buffer, size.into())?; if !complete { // The API docs don't say what happens when the buffer is not big enough... // Let's just bail.