miri/shims/windows/
env.rs
1use std::env;
2use std::ffi::{OsStr, OsString};
3use std::io::ErrorKind;
4
5use rustc_data_structures::fx::FxHashMap;
6
7use self::helpers::windows_check_buffer_size;
8use crate::*;
9
10#[derive(Default)]
11pub struct WindowsEnvVars {
12 map: FxHashMap<OsString, OsString>,
14}
15
16impl VisitProvenance for WindowsEnvVars {
17 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
18 let WindowsEnvVars { map: _ } = self;
19 }
20}
21
22impl WindowsEnvVars {
23 pub(crate) fn new<'tcx>(
24 _ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
25 env_vars: FxHashMap<OsString, OsString>,
26 ) -> InterpResult<'tcx, Self> {
27 interp_ok(Self { map: env_vars })
28 }
29
30 pub(crate) fn get<'tcx>(&self, name: &OsStr) -> InterpResult<'tcx, Option<OsString>> {
32 interp_ok(self.map.get(name).cloned())
33 }
34}
35
36impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
37pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
38 #[allow(non_snake_case)]
39 fn GetEnvironmentVariableW(
40 &mut self,
41 name_op: &OpTy<'tcx>, buf_op: &OpTy<'tcx>, size_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, Scalar> {
45 let this = self.eval_context_mut();
48 this.assert_target_os("windows", "GetEnvironmentVariableW");
49
50 let name_ptr = this.read_pointer(name_op)?;
51 let buf_ptr = this.read_pointer(buf_op)?;
52 let buf_size = this.read_scalar(size_op)?.to_u32()?; let name = this.read_os_str_from_wide_str(name_ptr)?;
55 interp_ok(match this.machine.env_vars.windows().map.get(&name).cloned() {
56 Some(val) => {
57 Scalar::from_u32(windows_check_buffer_size(this.write_os_str_to_wide_str(
58 &val,
59 buf_ptr,
60 buf_size.into(),
61 )?))
62 }
65 None => {
66 let envvar_not_found = this.eval_windows("c", "ERROR_ENVVAR_NOT_FOUND");
67 this.set_last_error(envvar_not_found)?;
68 Scalar::from_u32(0) }
70 })
71 }
72
73 #[allow(non_snake_case)]
74 fn GetEnvironmentStringsW(&mut self) -> InterpResult<'tcx, Pointer> {
75 let this = self.eval_context_mut();
76 this.assert_target_os("windows", "GetEnvironmentStringsW");
77
78 let mut env_vars = std::ffi::OsString::new();
81 for (name, value) in this.machine.env_vars.windows().map.iter() {
82 env_vars.push(name);
83 env_vars.push("=");
84 env_vars.push(value);
85 env_vars.push("\0");
86 }
87 let envblock_ptr =
90 this.alloc_os_str_as_wide_str(&env_vars, MiriMemoryKind::Runtime.into())?;
91 interp_ok(envblock_ptr)
93 }
94
95 #[allow(non_snake_case)]
96 fn FreeEnvironmentStringsW(&mut self, env_block_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
97 let this = self.eval_context_mut();
98 this.assert_target_os("windows", "FreeEnvironmentStringsW");
99
100 let env_block_ptr = this.read_pointer(env_block_op)?;
101 this.deallocate_ptr(env_block_ptr, None, MiriMemoryKind::Runtime.into())?;
102 interp_ok(Scalar::from_i32(1))
104 }
105
106 #[allow(non_snake_case)]
107 fn SetEnvironmentVariableW(
108 &mut self,
109 name_op: &OpTy<'tcx>, value_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, Scalar> {
112 let this = self.eval_context_mut();
113 this.assert_target_os("windows", "SetEnvironmentVariableW");
114
115 let name_ptr = this.read_pointer(name_op)?;
116 let value_ptr = this.read_pointer(value_op)?;
117
118 if this.ptr_is_null(name_ptr)? {
119 throw_ub_format!("pointer to environment variable name is NULL");
121 }
122
123 let name = this.read_os_str_from_wide_str(name_ptr)?;
124 if name.is_empty() {
125 throw_unsup_format!("environment variable name is an empty string");
126 } else if name.to_string_lossy().contains('=') {
127 throw_unsup_format!("environment variable name contains '='");
128 } else if this.ptr_is_null(value_ptr)? {
129 this.machine.env_vars.windows_mut().map.remove(&name);
131 interp_ok(this.eval_windows("c", "TRUE"))
132 } else {
133 let value = this.read_os_str_from_wide_str(value_ptr)?;
134 this.machine.env_vars.windows_mut().map.insert(name, value);
135 interp_ok(this.eval_windows("c", "TRUE"))
136 }
137 }
138
139 #[allow(non_snake_case)]
140 fn GetCurrentDirectoryW(
141 &mut self,
142 size_op: &OpTy<'tcx>, buf_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, Scalar> {
145 let this = self.eval_context_mut();
146 this.assert_target_os("windows", "GetCurrentDirectoryW");
147
148 let size = u64::from(this.read_scalar(size_op)?.to_u32()?);
149 let buf = this.read_pointer(buf_op)?;
150
151 if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
152 this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?;
153 this.set_last_error(ErrorKind::PermissionDenied)?;
154 return interp_ok(Scalar::from_u32(0));
155 }
156
157 match env::current_dir() {
159 Ok(cwd) => {
160 return interp_ok(Scalar::from_u32(windows_check_buffer_size(
163 this.write_path_to_wide_str(&cwd, buf, size)?,
164 )));
165 }
166 Err(e) => this.set_last_error(e)?,
167 }
168 interp_ok(Scalar::from_u32(0))
169 }
170
171 #[allow(non_snake_case)]
172 fn SetCurrentDirectoryW(
173 &mut self,
174 path_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, Scalar> {
176 let this = self.eval_context_mut();
179 this.assert_target_os("windows", "SetCurrentDirectoryW");
180
181 let path = this.read_path_from_wide_str(this.read_pointer(path_op)?)?;
182
183 if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
184 this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?;
185 this.set_last_error(ErrorKind::PermissionDenied)?;
186
187 return interp_ok(this.eval_windows("c", "FALSE"));
188 }
189
190 match env::set_current_dir(path) {
191 Ok(()) => interp_ok(this.eval_windows("c", "TRUE")),
192 Err(e) => {
193 this.set_last_error(e)?;
194 interp_ok(this.eval_windows("c", "FALSE"))
195 }
196 }
197 }
198
199 #[allow(non_snake_case)]
200 fn GetCurrentProcessId(&mut self) -> InterpResult<'tcx, Scalar> {
201 let this = self.eval_context_mut();
202 this.assert_target_os("windows", "GetCurrentProcessId");
203
204 interp_ok(Scalar::from_u32(this.get_pid()))
205 }
206
207 #[allow(non_snake_case)]
208 fn GetUserProfileDirectoryW(
209 &mut self,
210 token: &OpTy<'tcx>, buf: &OpTy<'tcx>, size: &OpTy<'tcx>, ) -> InterpResult<'tcx, Scalar> {
215 let this = self.eval_context_mut();
216 this.assert_target_os("windows", "GetUserProfileDirectoryW");
217 this.check_no_isolation("`GetUserProfileDirectoryW`")?;
218
219 let token = this.read_target_isize(token)?;
220 let buf = this.read_pointer(buf)?;
221 let size = this.deref_pointer_as(size, this.machine.layouts.u32)?;
222
223 if token != -4 {
224 throw_unsup_format!(
225 "GetUserProfileDirectoryW: only CURRENT_PROCESS_TOKEN is supported"
226 );
227 }
228
229 interp_ok(match directories::UserDirs::new() {
231 Some(dirs) => {
232 let home = dirs.home_dir();
233 let size_avail = if this.ptr_is_null(size.ptr())? {
234 0 } else {
236 this.read_scalar(&size)?.to_u32()?
237 };
238 let (success, len) = this.write_path_to_wide_str(home, buf, size_avail.into())?;
241 this.write_scalar(Scalar::from_u32(len.try_into().unwrap()), &size)?;
244 if success {
245 Scalar::from_i32(1) } else {
247 this.set_last_error(this.eval_windows("c", "ERROR_INSUFFICIENT_BUFFER"))?;
248 Scalar::from_i32(0) }
250 }
251 None => {
252 this.set_last_error(this.eval_windows("c", "ERROR_BAD_USER_PROFILE"))?;
254 Scalar::from_i32(0) }
256 })
257 }
258}