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