miri/shims/windows/
env.rsuse std::env;
use std::ffi::{OsStr, OsString};
use std::io::ErrorKind;
use rustc_data_structures::fx::FxHashMap;
use self::helpers::windows_check_buffer_size;
use crate::*;
#[derive(Default)]
pub struct WindowsEnvVars {
map: FxHashMap<OsString, OsString>,
}
impl VisitProvenance for WindowsEnvVars {
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
let WindowsEnvVars { map: _ } = self;
}
}
impl WindowsEnvVars {
pub(crate) fn new<'tcx>(
_ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
env_vars: FxHashMap<OsString, OsString>,
) -> InterpResult<'tcx, Self> {
interp_ok(Self { map: env_vars })
}
pub(crate) fn get<'tcx>(&self, name: &OsStr) -> InterpResult<'tcx, Option<OsString>> {
interp_ok(self.map.get(name).cloned())
}
}
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[allow(non_snake_case)]
fn GetEnvironmentVariableW(
&mut self,
name_op: &OpTy<'tcx>, buf_op: &OpTy<'tcx>, size_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "GetEnvironmentVariableW");
let name_ptr = this.read_pointer(name_op)?;
let buf_ptr = this.read_pointer(buf_op)?;
let buf_size = this.read_scalar(size_op)?.to_u32()?; let name = this.read_os_str_from_wide_str(name_ptr)?;
interp_ok(match this.machine.env_vars.windows().map.get(&name).cloned() {
Some(val) => {
Scalar::from_u32(windows_check_buffer_size(this.write_os_str_to_wide_str(
&val,
buf_ptr,
buf_size.into(),
)?))
}
None => {
let envvar_not_found = this.eval_windows("c", "ERROR_ENVVAR_NOT_FOUND");
this.set_last_error(envvar_not_found)?;
Scalar::from_u32(0) }
})
}
#[allow(non_snake_case)]
fn GetEnvironmentStringsW(&mut self) -> InterpResult<'tcx, Pointer> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "GetEnvironmentStringsW");
let mut env_vars = std::ffi::OsString::new();
for (name, value) in this.machine.env_vars.windows().map.iter() {
env_vars.push(name);
env_vars.push("=");
env_vars.push(value);
env_vars.push("\0");
}
let envblock_ptr =
this.alloc_os_str_as_wide_str(&env_vars, MiriMemoryKind::Runtime.into())?;
interp_ok(envblock_ptr)
}
#[allow(non_snake_case)]
fn FreeEnvironmentStringsW(&mut self, env_block_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "FreeEnvironmentStringsW");
let env_block_ptr = this.read_pointer(env_block_op)?;
this.deallocate_ptr(env_block_ptr, None, MiriMemoryKind::Runtime.into())?;
interp_ok(Scalar::from_i32(1))
}
#[allow(non_snake_case)]
fn SetEnvironmentVariableW(
&mut self,
name_op: &OpTy<'tcx>, value_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "SetEnvironmentVariableW");
let name_ptr = this.read_pointer(name_op)?;
let value_ptr = this.read_pointer(value_op)?;
if this.ptr_is_null(name_ptr)? {
throw_ub_format!("pointer to environment variable name is NULL");
}
let name = this.read_os_str_from_wide_str(name_ptr)?;
if name.is_empty() {
throw_unsup_format!("environment variable name is an empty string");
} else if name.to_string_lossy().contains('=') {
throw_unsup_format!("environment variable name contains '='");
} else if this.ptr_is_null(value_ptr)? {
this.machine.env_vars.windows_mut().map.remove(&name);
interp_ok(this.eval_windows("c", "TRUE"))
} else {
let value = this.read_os_str_from_wide_str(value_ptr)?;
this.machine.env_vars.windows_mut().map.insert(name, value);
interp_ok(this.eval_windows("c", "TRUE"))
}
}
#[allow(non_snake_case)]
fn GetCurrentDirectoryW(
&mut self,
size_op: &OpTy<'tcx>, buf_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "GetCurrentDirectoryW");
let size = u64::from(this.read_scalar(size_op)?.to_u32()?);
let buf = this.read_pointer(buf_op)?;
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?;
this.set_last_error(ErrorKind::PermissionDenied)?;
return interp_ok(Scalar::from_u32(0));
}
match env::current_dir() {
Ok(cwd) => {
return interp_ok(Scalar::from_u32(windows_check_buffer_size(
this.write_path_to_wide_str(&cwd, buf, size)?,
)));
}
Err(e) => this.set_last_error(e)?,
}
interp_ok(Scalar::from_u32(0))
}
#[allow(non_snake_case)]
fn SetCurrentDirectoryW(
&mut self,
path_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "SetCurrentDirectoryW");
let path = this.read_path_from_wide_str(this.read_pointer(path_op)?)?;
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?;
this.set_last_error(ErrorKind::PermissionDenied)?;
return interp_ok(this.eval_windows("c", "FALSE"));
}
match env::set_current_dir(path) {
Ok(()) => interp_ok(this.eval_windows("c", "TRUE")),
Err(e) => {
this.set_last_error(e)?;
interp_ok(this.eval_windows("c", "FALSE"))
}
}
}
#[allow(non_snake_case)]
fn GetCurrentProcessId(&mut self) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "GetCurrentProcessId");
interp_ok(Scalar::from_u32(this.get_pid()))
}
#[allow(non_snake_case)]
fn GetUserProfileDirectoryW(
&mut self,
token: &OpTy<'tcx>, buf: &OpTy<'tcx>, size: &OpTy<'tcx>, ) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "GetUserProfileDirectoryW");
this.check_no_isolation("`GetUserProfileDirectoryW`")?;
let token = this.read_target_isize(token)?;
let buf = this.read_pointer(buf)?;
let size = this.deref_pointer(size)?;
if token != -4 {
throw_unsup_format!(
"GetUserProfileDirectoryW: only CURRENT_PROCESS_TOKEN is supported"
);
}
interp_ok(match directories::UserDirs::new() {
Some(dirs) => {
let home = dirs.home_dir();
let size_avail = if this.ptr_is_null(size.ptr())? {
0 } else {
this.read_scalar(&size)?.to_u32()?
};
let (success, len) = this.write_path_to_wide_str(home, buf, size_avail.into())?;
this.write_scalar(Scalar::from_u32(len.try_into().unwrap()), &size)?;
if success {
Scalar::from_i32(1) } else {
this.set_last_error(this.eval_windows("c", "ERROR_INSUFFICIENT_BUFFER"))?;
Scalar::from_i32(0) }
}
None => {
this.set_last_error(this.eval_windows("c", "ERROR_BAD_USER_PROFILE"))?;
Scalar::from_i32(0) }
})
}
}