Skip to content

stop relying on c_str/wide_str helpers in rustc #1805

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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