diff --git a/src/helpers.rs b/src/helpers.rs index 7215cb4b0c..ef5ea94478 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -566,6 +566,51 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Duration::new(seconds, nanoseconds) }) } + + fn read_c_str<'a>(&'a self, sptr: Scalar) -> InterpResult<'tcx, &'a [u8]> + where + 'tcx: 'a, + 'mir: 'a, + { + let this = self.eval_context_ref(); + let size1 = Size::from_bytes(1); + let ptr = this.force_ptr(sptr)?; // We need to read at least 1 byte, so we can eagerly get a ptr. + + // Step 1: determine the length. + let alloc = this.memory.get_raw(ptr.alloc_id)?; + let mut len = Size::ZERO; + loop { + let byte = alloc.read_scalar(this, ptr.offset(len, this)?, size1)?.to_u8()?; + if byte == 0 { + break; + } else { + len = len + size1; + } + } + + // Step 2: get the bytes. + this.memory.read_bytes(ptr.into(), len) + } + + fn read_wide_str(&self, sptr: Scalar) -> InterpResult<'tcx, Vec> { + let this = self.eval_context_ref(); + let size2 = Size::from_bytes(2); + + let mut ptr = this.force_ptr(sptr)?; // We need to read at least 1 wchar, so we can eagerly get a ptr. + let mut wchars = Vec::new(); + let alloc = this.memory.get_raw(ptr.alloc_id)?; + loop { + let wchar = alloc.read_scalar(this, ptr, size2)?.to_u16()?; + if wchar == 0 { + break; + } else { + wchars.push(wchar); + ptr = ptr.offset(size2, this)?; + } + } + + Ok(wchars) + } } /// Check that the number of args is what we expect. diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 6867871794..47d939e697 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -405,7 +405,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx check_abi(abi, Abi::C { unwind: false })?; let &[ref ptr] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; - let n = this.memory.read_c_str(ptr)?.len(); + let n = this.read_c_str(ptr)?.len(); this.write_scalar(Scalar::from_machine_usize(u64::try_from(n).unwrap(), this), dest)?; } diff --git a/src/shims/os_str.rs b/src/shims/os_str.rs index 22e3806ad3..f1f14fa828 100644 --- a/src/shims/os_str.rs +++ b/src/shims/os_str.rs @@ -9,7 +9,7 @@ use std::os::unix::ffi::{OsStrExt, OsStringExt}; #[cfg(windows)] use std::os::windows::ffi::{OsStrExt, OsStringExt}; -use rustc_target::abi::LayoutOf; +use rustc_target::abi::{LayoutOf, Size}; use crate::*; @@ -50,19 +50,19 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mi pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { /// Helper function to read an OsString from a null-terminated sequence of bytes, which is what /// the Unix APIs usually handle. - fn read_os_str_from_c_str<'a>(&'a self, scalar: Scalar) -> InterpResult<'tcx, &'a OsStr> + fn read_os_str_from_c_str<'a>(&'a self, sptr: Scalar) -> InterpResult<'tcx, &'a OsStr> where 'tcx: 'a, 'mir: 'a, { let this = self.eval_context_ref(); - let bytes = this.memory.read_c_str(scalar)?; + let bytes = this.read_c_str(sptr)?; bytes_to_os_str(bytes) } /// Helper function to read an OsString from a 0x0000-terminated sequence of u16, /// which is what the Windows APIs usually handle. - fn read_os_str_from_wide_str<'a>(&'a self, scalar: Scalar) -> InterpResult<'tcx, OsString> + fn read_os_str_from_wide_str<'a>(&'a self, sptr: Scalar) -> InterpResult<'tcx, OsString> where 'tcx: 'a, 'mir: 'a, @@ -78,7 +78,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(s.into()) } - let u16_vec = self.eval_context_ref().memory.read_wide_str(scalar)?; + let u16_vec = self.eval_context_ref().read_wide_str(sptr)?; u16vec_to_osstring(u16_vec) } @@ -90,7 +90,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_os_str_to_c_str( &mut self, os_str: &OsStr, - scalar: Scalar, + sptr: Scalar, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { let bytes = os_str_to_bytes(os_str)?; @@ -102,7 +102,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } self.eval_context_mut() .memory - .write_bytes(scalar, bytes.iter().copied().chain(iter::once(0u8)))?; + .write_bytes(sptr, bytes.iter().copied().chain(iter::once(0u8)))?; Ok((true, string_length)) } @@ -114,7 +114,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_os_str_to_wide_str( &mut self, os_str: &OsStr, - scalar: Scalar, + sptr: Scalar, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { #[cfg(windows)] @@ -136,15 +136,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required // 0x0000 terminator to memory would cause an out-of-bounds access. let string_length = u64::try_from(u16_vec.len()).unwrap(); - if size <= string_length { + let string_length = string_length.checked_add(1).unwrap(); + if size < string_length { return Ok((false, string_length)); } // Store the UTF-16 string. - self.eval_context_mut() - .memory - .write_u16s(scalar, u16_vec.into_iter().chain(iter::once(0x0000)))?; - Ok((true, string_length)) + let size2 = Size::from_bytes(2); + let this = self.eval_context_mut(); + let tcx = &*this.tcx; + let ptr = this.force_ptr(sptr)?; // we need to write at least the 0 terminator + let alloc = this.memory.get_raw_mut(ptr.alloc_id)?; + for (offset, wchar) in u16_vec.into_iter().chain(iter::once(0x0000)).enumerate() { + let offset = u64::try_from(offset).unwrap(); + alloc.write_scalar( + tcx, + ptr.offset(size2 * offset, tcx)?, + Scalar::from_u16(wchar).into(), + size2, + )?; + } + Ok((true, string_length - 1)) } /// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes. @@ -178,13 +190,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } /// Read a null-terminated sequence of bytes, and perform path separator conversion if needed. - fn read_path_from_c_str<'a>(&'a self, scalar: Scalar) -> InterpResult<'tcx, Cow<'a, Path>> + fn read_path_from_c_str<'a>(&'a self, sptr: Scalar) -> InterpResult<'tcx, Cow<'a, Path>> where 'tcx: 'a, 'mir: 'a, { let this = self.eval_context_ref(); - let os_str = this.read_os_str_from_c_str(scalar)?; + let os_str = this.read_os_str_from_c_str(sptr)?; Ok(match this.convert_path_separator(Cow::Borrowed(os_str), PathConversion::TargetToHost) { Cow::Borrowed(x) => Cow::Borrowed(Path::new(x)), @@ -193,9 +205,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } /// Read a null-terminated sequence of `u16`s, and perform path separator conversion if needed. - fn read_path_from_wide_str(&self, scalar: Scalar) -> InterpResult<'tcx, PathBuf> { + fn read_path_from_wide_str(&self, sptr: Scalar) -> InterpResult<'tcx, PathBuf> { let this = self.eval_context_ref(); - let os_str = this.read_os_str_from_wide_str(scalar)?; + let os_str = this.read_os_str_from_wide_str(sptr)?; Ok(this .convert_path_separator(Cow::Owned(os_str), PathConversion::TargetToHost) @@ -208,13 +220,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_path_to_c_str( &mut self, path: &Path, - scalar: Scalar, + sptr: Scalar, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { let this = self.eval_context_mut(); let os_str = this .convert_path_separator(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget); - this.write_os_str_to_c_str(&os_str, scalar, size) + this.write_os_str_to_c_str(&os_str, sptr, size) } /// Write a Path to the machine memory (as a null-terminated sequence of `u16`s), @@ -222,13 +234,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_path_to_wide_str( &mut self, path: &Path, - scalar: Scalar, + sptr: Scalar, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { let this = self.eval_context_mut(); let os_str = this .convert_path_separator(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget); - this.write_os_str_to_wide_str(&os_str, scalar, size) + this.write_os_str_to_wide_str(&os_str, sptr, size) } fn convert_path_separator<'a>( diff --git a/src/shims/posix/foreign_items.rs b/src/shims/posix/foreign_items.rs index b3d53cdc10..52b41b49bd 100644 --- a/src/shims/posix/foreign_items.rs +++ b/src/shims/posix/foreign_items.rs @@ -187,7 +187,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let &[ref handle, ref symbol] = check_arg_count(args)?; this.read_scalar(handle)?.to_machine_usize(this)?; let symbol = this.read_scalar(symbol)?.check_init()?; - let symbol_name = this.memory.read_c_str(symbol)?; + let symbol_name = this.read_c_str(symbol)?; if let Some(dlsym) = Dlsym::from_str(symbol_name, &this.tcx.sess.target.os)? { let ptr = this.memory.create_fn_alloc(FnVal::Other(dlsym)); this.write_scalar(Scalar::from(ptr), dest)?; diff --git a/src/shims/posix/thread.rs b/src/shims/posix/thread.rs index 214a2ce411..1e45978489 100644 --- a/src/shims/posix/thread.rs +++ b/src/shims/posix/thread.rs @@ -111,7 +111,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let option = this.read_scalar(option)?.to_i32()?; if option == this.eval_libc_i32("PR_SET_NAME")? { let address = this.read_scalar(arg2)?.check_init()?; - let mut name = this.memory.read_c_str(address)?.to_owned(); + let mut name = this.read_c_str(address)?.to_owned(); // The name should be no more than 16 bytes, including the null // byte. Since `read_c_str` returns the string without the null // byte, we need to truncate to 15. @@ -134,7 +134,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); this.assert_target_os("macos", "pthread_setname_np"); - let name = this.memory.read_c_str(name)?.to_owned(); + let name = this.read_c_str(name)?.to_owned(); this.set_active_thread_name(name); Ok(()) diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index f29870ff7c..b246ccc33c 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -271,7 +271,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx #[allow(non_snake_case)] let &[ref hModule, ref lpProcName] = check_arg_count(args)?; this.read_scalar(hModule)?.to_machine_isize(this)?; - let name = this.memory.read_c_str(this.read_scalar(lpProcName)?.check_init()?)?; + let name = this.read_c_str(this.read_scalar(lpProcName)?.check_init()?)?; if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? { let ptr = this.memory.create_fn_alloc(FnVal::Other(dlsym)); this.write_scalar(Scalar::from(ptr), dest)?;