|
1 | 1 | use std::mem;
|
| 2 | +use std::ffi::{OsStr, OsString}; |
2 | 3 |
|
3 | 4 | use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};
|
4 | 5 | use rustc::mir;
|
@@ -402,4 +403,60 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
402 | 403 | }
|
403 | 404 | }
|
404 | 405 | }
|
| 406 | + |
| 407 | + /// Helper function to read an OsString from a null-terminated sequence of bytes, which is what |
| 408 | + /// the Unix APIs usually handle. |
| 409 | + fn read_os_string_from_c_string(&mut self, scalar: Scalar<Tag>) -> InterpResult<'tcx, OsString> { |
| 410 | + let bytes = self.eval_context_mut().memory.read_c_str(scalar)?; |
| 411 | + Ok(bytes_to_os_str(bytes)?.into()) |
| 412 | + } |
| 413 | + |
| 414 | + /// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what |
| 415 | + /// the Unix APIs usually handle. This function returns `Ok(false)` without trying to write if |
| 416 | + /// `size` is not large enough to fit the contents of `os_string` plus a null terminator. It |
| 417 | + /// returns `Ok(true)` if the writing process was successful. |
| 418 | + fn write_os_str_to_c_string( |
| 419 | + &mut self, |
| 420 | + os_str: &OsStr, |
| 421 | + scalar: Scalar<Tag>, |
| 422 | + size: u64 |
| 423 | + ) -> InterpResult<'tcx, bool> { |
| 424 | + let bytes = os_str_to_bytes(os_str)?; |
| 425 | + // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null |
| 426 | + // terminator to memory using the `ptr` pointer would cause an overflow. |
| 427 | + if size <= bytes.len() as u64 { |
| 428 | + return Ok(false); |
| 429 | + } |
| 430 | + // FIXME: We should use `Iterator::chain` instead when rust-lang/rust#65704 lands. |
| 431 | + self.eval_context_mut().memory.write_bytes(scalar, [bytes, &[0]].concat())?; |
| 432 | + Ok(true) |
| 433 | + } |
| 434 | +} |
| 435 | + |
| 436 | +#[cfg(target_os = "unix")] |
| 437 | +fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> { |
| 438 | + std::os::unix::ffi::OsStringExt::into_bytes(os_str) |
| 439 | +} |
| 440 | + |
| 441 | +#[cfg(target_os = "unix")] |
| 442 | +fn bytes_to_os_str<'tcx, 'a>(bytes: &'a[u8]) -> InterpResult<'tcx, &'a OsStr> { |
| 443 | + Ok(std::os::unix::ffi::OsStringExt::from_bytes(bytes)) |
| 444 | +} |
| 445 | + |
| 446 | +// On non-unix platforms the best we can do to transform bytes from/to OS strings is to do the |
| 447 | +// intermediate transformation into strings. Which invalidates non-utf8 paths that are actually |
| 448 | +// valid. |
| 449 | +#[cfg(not(target_os = "unix"))] |
| 450 | +fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> { |
| 451 | + os_str |
| 452 | + .to_str() |
| 453 | + .map(|s| s.as_bytes()) |
| 454 | + .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into()) |
| 455 | +} |
| 456 | + |
| 457 | +#[cfg(not(target_os = "unix"))] |
| 458 | +fn bytes_to_os_str<'tcx, 'a>(bytes: &'a[u8]) -> InterpResult<'tcx, &'a OsStr> { |
| 459 | + let s = std::str::from_utf8(bytes) |
| 460 | + .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", bytes))?; |
| 461 | + Ok(&OsStr::new(s)) |
405 | 462 | }
|
0 commit comments