diff --git a/src/helpers.rs b/src/helpers.rs index 40c2008ac9..795f21ddb9 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -982,29 +982,46 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok((true, string_length)) } - /// Read a sequence of u16 until the first null terminator. - fn read_wide_str(&self, mut ptr: Pointer>) -> InterpResult<'tcx, Vec> { + /// Helper function to read a sequence of unsigned integers of the given size and alignment + /// until the first null terminator. + fn read_c_str_with_char_size( + &self, + mut ptr: Pointer>, + size: Size, + align: Align, + ) -> InterpResult<'tcx, Vec> + where + T: TryFrom, + >::Error: std::fmt::Debug, + { + assert_ne!(size, Size::ZERO); + let this = self.eval_context_ref(); - let size2 = Size::from_bytes(2); - this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?; + + this.check_ptr_align(ptr, align)?; let mut wchars = Vec::new(); loop { // FIXME: We are re-getting the allocation each time around the loop. // Would be nice if we could somehow "extend" an existing AllocRange. - let alloc = this.get_ptr_alloc(ptr, size2)?.unwrap(); // not a ZST, so we will get a result - let wchar = alloc.read_integer(alloc_range(Size::ZERO, size2))?.to_u16()?; - if wchar == 0 { + let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); // not a ZST, so we will get a result + let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?; + if wchar_int == 0 { break; } else { - wchars.push(wchar); - ptr = ptr.offset(size2, this)?; + wchars.push(wchar_int.try_into().unwrap()); + ptr = ptr.offset(size, this)?; } } Ok(wchars) } + /// Read a sequence of u16 until the first null terminator. + fn read_wide_str(&self, ptr: Pointer>) -> InterpResult<'tcx, Vec> { + self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap()) + } + /// Helper function to write a sequence of u16 with an added 0x0000-terminator, 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 @@ -1037,6 +1054,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok((true, string_length)) } + /// Read a sequence of wchar_t until the first null terminator. + /// Always returns a `Vec` no matter the size of `wchar_t`. + fn read_wchar_t_str(&self, ptr: Pointer>) -> InterpResult<'tcx, Vec> { + let this = self.eval_context_ref(); + let wchar_t = this.libc_ty_layout("wchar_t"); + self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi) + } + /// Check that the ABI is what we expect. fn check_abi<'a>(&self, abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> { if abi != exp_abi { diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index c51a27b745..28028479ac 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -657,6 +657,16 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest, )?; } + "wcslen" => { + let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let ptr = this.read_pointer(ptr)?; + // This reads at least 1 byte, so we are already enforcing that this is a valid pointer. + let n = this.read_wchar_t_str(ptr)?.len(); + this.write_scalar( + Scalar::from_target_usize(u64::try_from(n).unwrap(), this), + dest, + )?; + } "memcpy" => { let [ptr_dest, ptr_src, n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/tests/pass-dep/wcslen.rs b/tests/pass-dep/wcslen.rs new file mode 100644 index 0000000000..c5c9d99247 --- /dev/null +++ b/tests/pass-dep/wcslen.rs @@ -0,0 +1,20 @@ +fn to_c_wchar_t_str(s: &str) -> Vec { + let mut r = Vec::::new(); + for c in s.bytes() { + if c == 0 { + panic!("can't contain a null character"); + } + if c >= 128 { + panic!("only ASCII supported"); + } + r.push(c.into()); + } + r.push(0); + r +} + +pub fn main() { + let s = to_c_wchar_t_str("Rust"); + let len = unsafe { libc::wcslen(s.as_ptr()) }; + assert_eq!(len, 4); +}