Skip to content

Commit

Permalink
stop relying on c_str/wide_str helpers in rustc
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed May 17, 2021
1 parent 516e905 commit 4f171d7
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 26 deletions.
45 changes: 45 additions & 0 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Tag>) -> 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<Tag>) -> InterpResult<'tcx, Vec<u16>> {
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.
Expand Down
2 changes: 1 addition & 1 deletion src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
}

Expand Down
54 changes: 33 additions & 21 deletions src/shims/os_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand Down Expand Up @@ -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<Tag>) -> InterpResult<'tcx, &'a OsStr>
fn read_os_str_from_c_str<'a>(&'a self, sptr: Scalar<Tag>) -> 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<Tag>) -> InterpResult<'tcx, OsString>
fn read_os_str_from_wide_str<'a>(&'a self, sptr: Scalar<Tag>) -> InterpResult<'tcx, OsString>
where
'tcx: 'a,
'mir: 'a,
Expand All @@ -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)
}

Expand All @@ -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<Tag>,
sptr: Scalar<Tag>,
size: u64,
) -> InterpResult<'tcx, (bool, u64)> {
let bytes = os_str_to_bytes(os_str)?;
Expand All @@ -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))
}

Expand All @@ -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<Tag>,
sptr: Scalar<Tag>,
size: u64,
) -> InterpResult<'tcx, (bool, u64)> {
#[cfg(windows)]
Expand All @@ -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.
Expand Down Expand Up @@ -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<Tag>) -> InterpResult<'tcx, Cow<'a, Path>>
fn read_path_from_c_str<'a>(&'a self, sptr: Scalar<Tag>) -> 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)),
Expand All @@ -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<Tag>) -> InterpResult<'tcx, PathBuf> {
fn read_path_from_wide_str(&self, sptr: Scalar<Tag>) -> 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)
Expand All @@ -208,27 +220,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
fn write_path_to_c_str(
&mut self,
path: &Path,
scalar: Scalar<Tag>,
sptr: Scalar<Tag>,
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),
/// adjusting path separators if needed.
fn write_path_to_wide_str(
&mut self,
path: &Path,
scalar: Scalar<Tag>,
sptr: Scalar<Tag>,
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>(
Expand Down
2 changes: 1 addition & 1 deletion src/shims/posix/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down
4 changes: 2 additions & 2 deletions src/shims/posix/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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(())
Expand Down
2 changes: 1 addition & 1 deletion src/shims/windows/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down

0 comments on commit 4f171d7

Please sign in to comment.