11pub mod convert;
22
33use std::cmp;
4+ use std::iter;
45use std::mem;
56use std::num::NonZeroUsize;
67use std::time::Duration;
@@ -107,7 +108,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
107108 /// Gets an instance for a path.
108109 fn resolve_path(&self, path: &[&str]) -> ty::Instance<'tcx> {
109110 self.try_resolve_path(path)
110- .unwrap_or_else(|| panic!("failed to find required Rust item: {:?}", path ))
111+ .unwrap_or_else(|| panic!("failed to find required Rust item: {path :?}"))
111112 }
112113
113114 /// Evaluates the scalar at the specified path. Returns Some(val)
@@ -505,7 +506,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
505506 RejectOpWith::WarningWithoutBacktrace => {
506507 this.tcx
507508 .sess
508- .warn(& format!("{} was made to return an error due to isolation", op_name ));
509+ .warn(format!("{op_name } was made to return an error due to isolation"));
509510 Ok(())
510511 }
511512 RejectOpWith::Warning => {
@@ -735,6 +736,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
735736 })
736737 }
737738
739+ /// Read a sequence of bytes until the first null terminator.
738740 fn read_c_str<'a>(&'a self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, &'a [u8]>
739741 where
740742 'tcx: 'a,
@@ -761,6 +763,30 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
761763 this.read_bytes_ptr_strip_provenance(ptr, len)
762764 }
763765
766+ /// Helper function to write a sequence of bytes with an added null-terminator, which is what
767+ /// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
768+ /// to write if `size` is not large enough to fit the contents of `c_str` plus a null
769+ /// terminator. It returns `Ok((true, length))` if the writing process was successful. The
770+ /// string length returned does include the null terminator.
771+ fn write_c_str(
772+ &mut self,
773+ c_str: &[u8],
774+ ptr: Pointer<Option<Provenance>>,
775+ size: u64,
776+ ) -> InterpResult<'tcx, (bool, u64)> {
777+ // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
778+ // terminator to memory using the `ptr` pointer would cause an out-of-bounds access.
779+ let string_length = u64::try_from(c_str.len()).unwrap();
780+ let string_length = string_length.checked_add(1).unwrap();
781+ if size < string_length {
782+ return Ok((false, string_length));
783+ }
784+ self.eval_context_mut()
785+ .write_bytes_ptr(ptr, c_str.iter().copied().chain(iter::once(0u8)))?;
786+ Ok((true, string_length))
787+ }
788+
789+ /// Read a sequence of u16 until the first null terminator.
764790 fn read_wide_str(&self, mut ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, Vec<u16>> {
765791 let this = self.eval_context_ref();
766792 let size2 = Size::from_bytes(2);
@@ -783,6 +809,39 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
783809 Ok(wchars)
784810 }
785811
812+ /// Helper function to write a sequence of u16 with an added 0x0000-terminator, which is what
813+ /// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying
814+ /// to write if `size` is not large enough to fit the contents of `os_string` plus a null
815+ /// terminator. It returns `Ok((true, length))` if the writing process was successful. The
816+ /// string length returned does include the null terminator. Length is measured in units of
817+ /// `u16.`
818+ fn write_wide_str(
819+ &mut self,
820+ wide_str: &[u16],
821+ ptr: Pointer<Option<Provenance>>,
822+ size: u64,
823+ ) -> InterpResult<'tcx, (bool, u64)> {
824+ // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required
825+ // 0x0000 terminator to memory would cause an out-of-bounds access.
826+ let string_length = u64::try_from(wide_str.len()).unwrap();
827+ let string_length = string_length.checked_add(1).unwrap();
828+ if size < string_length {
829+ return Ok((false, string_length));
830+ }
831+
832+ // Store the UTF-16 string.
833+ let size2 = Size::from_bytes(2);
834+ let this = self.eval_context_mut();
835+ let mut alloc = this
836+ .get_ptr_alloc_mut(ptr, size2 * string_length, Align::from_bytes(2).unwrap())?
837+ .unwrap(); // not a ZST, so we will get a result
838+ for (offset, wchar) in wide_str.iter().copied().chain(iter::once(0x0000)).enumerate() {
839+ let offset = u64::try_from(offset).unwrap();
840+ alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?;
841+ }
842+ Ok((true, string_length))
843+ }
844+
786845 /// Check that the ABI is what we expect.
787846 fn check_abi<'a>(&self, abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> {
788847 if self.eval_context_ref().machine.enforce_abi && abi != exp_abi {
0 commit comments