Skip to content

Add helper 'alloc_os_str_as_c_str' and use it in env_var emulation #1131

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
Dec 30, 2019
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
14 changes: 14 additions & 0 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
.write_bytes(scalar, bytes.iter().copied().chain(iter::once(0u8)))?;
Ok(true)
}

fn alloc_os_str_as_c_str(
&mut self,
os_str: &OsStr,
memkind: MemoryKind<MiriMemoryKind>
) -> Pointer<Tag> {
let size = os_str.len() as u64 + 1; // Make space for `0` terminator.
let this = self.eval_context_mut();

let arg_type = this.tcx.mk_array(this.tcx.types.u8, size);
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind);
self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap();
arg_place.ptr.assert_ptr()
}
}

pub fn immty_from_int_checked<'tcx>(
Expand Down
43 changes: 21 additions & 22 deletions src/shims/env.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
use std::collections::HashMap;
use std::ffi::OsString;
use std::ffi::{OsString, OsStr};
use std::env;

use crate::stacked_borrows::Tag;
use crate::*;

use rustc::ty::layout::Size;
use rustc_mir::interpret::{Memory, Pointer};
use rustc_mir::interpret::Pointer;

#[derive(Default)]
pub struct EnvVars {
/// Stores pointers to the environment variables. These variables must be stored as
/// null-terminated C strings with the `"{name}={value}"` format.
map: HashMap<Vec<u8>, Pointer<Tag>>,
map: HashMap<OsString, Pointer<Tag>>,
}

impl EnvVars {
Expand All @@ -30,24 +30,23 @@ impl EnvVars {
for (name, value) in env::vars() {
if !excluded_env_vars.contains(&name) {
let var_ptr =
alloc_env_var(name.as_bytes(), value.as_bytes(), &mut ecx.memory);
ecx.machine.env_vars.map.insert(name.into_bytes(), var_ptr);
alloc_env_var_as_c_str(name.as_ref(), value.as_ref(), ecx);
ecx.machine.env_vars.map.insert(OsString::from(name), var_ptr);
}
}
}
}
}

fn alloc_env_var<'mir, 'tcx>(
name: &[u8],
value: &[u8],
memory: &mut Memory<'mir, 'tcx, Evaluator<'tcx>>,
fn alloc_env_var_as_c_str<'mir, 'tcx>(
name: &OsStr,
value: &OsStr,
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
) -> Pointer<Tag> {
let mut bytes = name.to_vec();
bytes.push(b'=');
bytes.extend_from_slice(value);
bytes.push(0);
memory.allocate_static_bytes(bytes.as_slice(), MiriMemoryKind::Env.into())
let mut name_osstring = name.to_os_string();
name_osstring.push("=");
name_osstring.push(value);
ecx.alloc_os_str_as_c_str(name_osstring.as_os_str(), MiriMemoryKind::Env.into())
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
Expand All @@ -56,7 +55,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let this = self.eval_context_mut();

let name_ptr = this.read_scalar(name_op)?.not_undef()?;
let name = this.memory.read_c_str(name_ptr)?;
let name = this.read_os_str_from_c_str(name_ptr)?;
Ok(match this.machine.env_vars.map.get(name) {
// The offset is used to strip the "{name}=" part of the string.
Some(var_ptr) => {
Expand All @@ -71,20 +70,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
name_op: OpTy<'tcx, Tag>,
value_op: OpTy<'tcx, Tag>,
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let mut this = self.eval_context_mut();

let name_ptr = this.read_scalar(name_op)?.not_undef()?;
let value_ptr = this.read_scalar(value_op)?.not_undef()?;
let value = this.memory.read_c_str(value_ptr)?;
let value = this.read_os_str_from_c_str(value_ptr)?;
let mut new = None;
if !this.is_null(name_ptr)? {
let name = this.memory.read_c_str(name_ptr)?;
if !name.is_empty() && !name.contains(&b'=') {
let name = this.read_os_str_from_c_str(name_ptr)?;
if !name.is_empty() && !name.to_string_lossy().contains('=') {
new = Some((name.to_owned(), value.to_owned()));
}
}
if let Some((name, value)) = new {
let var_ptr = alloc_env_var(&name, &value, &mut this.memory);
let var_ptr = alloc_env_var_as_c_str(&name, &value, &mut this);
if let Some(var) = this.machine.env_vars.map.insert(name.to_owned(), var_ptr) {
this.memory
.deallocate(var, None, MiriMemoryKind::Env.into())?;
Expand All @@ -101,8 +100,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let name_ptr = this.read_scalar(name_op)?.not_undef()?;
let mut success = None;
if !this.is_null(name_ptr)? {
let name = this.memory.read_c_str(name_ptr)?.to_owned();
if !name.is_empty() && !name.contains(&b'=') {
let name = this.read_os_str_from_c_str(name_ptr)?.to_owned();
if !name.is_empty() && !name.to_string_lossy().contains('=') {
success = Some(this.machine.env_vars.map.remove(&name));
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -956,10 +956,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_null(dest)?;
}
"GetEnvironmentVariableW" => {
// args[0] : LPCWSTR lpName (32-bit ptr to a const string of 16-bit Unicode chars)
// args[1] : LPWSTR lpBuffer (32-bit pointer to a string of 16-bit Unicode chars)
// lpBuffer : ptr to buffer that receives contents of the env_var as a null-terminated string.
// Return `# of chars` stored in the buffer pointed to by lpBuffer, excluding null-terminator.
// Return 0 upon failure.

// This is not the env var you are looking for.
this.set_last_error(Scalar::from_u32(203))?; // ERROR_ENVVAR_NOT_FOUND
this.write_null(dest)?;
}
"SetEnvironmentVariableW" => {
// args[0] : LPCWSTR lpName (32-bit ptr to a const string of 16-bit Unicode chars)
// args[1] : LPCWSTR lpValue (32-bit ptr to a const string of 16-bit Unicode chars)
// Return nonzero if success, else return 0.
throw_unsup_format!("can't set environment variable on Windows");
}
"GetCommandLineW" => {
this.write_scalar(
this.machine.cmd_line.expect("machine must be initialized"),
Expand Down