1 | use std::env; |
2 | use std::ffi::{OsStr, OsString}; |
3 | use std::io::ErrorKind; |
4 | |
5 | use rustc_data_structures::fx::FxHashMap; |
6 | |
7 | use self::helpers::windows_check_buffer_size; |
8 | use crate::*; |
9 | |
10 | #[derive (Default)] |
11 | pub struct WindowsEnvVars { |
12 | /// Stores the environment variables. |
13 | map: FxHashMap<OsString, OsString>, |
14 | } |
15 | |
16 | impl VisitProvenance for WindowsEnvVars { |
17 | fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { |
18 | let WindowsEnvVars { map: _ } = self; |
19 | } |
20 | } |
21 | |
22 | impl 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 | /// Implementation detail for [`InterpCx::get_env_var`]. |
31 | pub(crate) fn get<'tcx>(&self, name: &OsStr) -> InterpResult<'tcx, Option<OsString>> { |
32 | interp_ok(self.map.get(name).cloned()) |
33 | } |
34 | } |
35 | |
36 | impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} |
37 | pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
38 | #[allow (non_snake_case)] |
39 | fn GetEnvironmentVariableW( |
40 | &mut self, |
41 | name_op: &OpTy<'tcx>, // LPCWSTR |
42 | buf_op: &OpTy<'tcx>, // LPWSTR |
43 | size_op: &OpTy<'tcx>, // DWORD |
44 | ) -> InterpResult<'tcx, Scalar> { |
45 | // ^ Returns DWORD (u32 on Windows) |
46 | |
47 | 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()?; // in characters |
53 | |
54 | 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 | // This can in fact return 0. It is up to the caller to set last_error to 0 |
63 | // beforehand and check it afterwards to exclude that case. |
64 | } |
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) // return zero upon failure |
69 | } |
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 | // Info on layout of environment blocks in Windows: |
79 | // https://docs.microsoft.com/en-us/windows/win32/procthread/environment-variables |
80 | 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 | // Allocate environment block & Store environment variables to environment block. |
88 | // Final null terminator(block terminator) is added by `alloc_os_str_to_wide_str`. |
89 | let envblock_ptr = |
90 | this.alloc_os_str_as_wide_str(&env_vars, MiriMemoryKind::Runtime.into())?; |
91 | // If the function succeeds, the return value is a pointer to the environment block of the current process. |
92 | 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 | // If the function succeeds, the return value is nonzero. |
103 | interp_ok(Scalar::from_i32(1)) |
104 | } |
105 | |
106 | #[allow (non_snake_case)] |
107 | fn SetEnvironmentVariableW( |
108 | &mut self, |
109 | name_op: &OpTy<'tcx>, // LPCWSTR |
110 | value_op: &OpTy<'tcx>, // LPCWSTR |
111 | ) -> 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 | // ERROR CODE is not clearly explained in docs.. For now, throw UB instead. |
120 | 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 | // Delete environment variable `{name}` if it exists. |
130 | 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>, // DWORD |
143 | buf_op: &OpTy<'tcx>, // LPTSTR |
144 | ) -> 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 | // If we cannot get the current directory, we return 0 |
158 | match env::current_dir() { |
159 | Ok(cwd) => { |
160 | // This can in fact return 0. It is up to the caller to set last_error to 0 |
161 | // beforehand and check it afterwards to exclude that case. |
162 | 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>, // LPCTSTR |
175 | ) -> InterpResult<'tcx, Scalar> { |
176 | // ^ Returns BOOL (i32 on Windows) |
177 | |
178 | 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>, // HANDLE |
211 | buf: &OpTy<'tcx>, // LPWSTR |
212 | size: &OpTy<'tcx>, // LPDWORD |
213 | ) -> InterpResult<'tcx, Scalar> // returns BOOL |
214 | { |
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 | // See <https://learn.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-getuserprofiledirectoryw> for docs. |
230 | 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 // if the buf pointer is null, we can't write to it; `size` will be updated to the required length |
235 | } else { |
236 | this.read_scalar(&size)?.to_u32()? |
237 | }; |
238 | // Of course we cannot use `windows_check_buffer_size` here since this uses |
239 | // a different method for dealing with a too-small buffer than the other functions... |
240 | let (success, len) = this.write_path_to_wide_str(home, buf, size_avail.into())?; |
241 | // The Windows docs just say that this is written on failure. But std |
242 | // seems to rely on it always being written. |
243 | this.write_scalar(Scalar::from_u32(len.try_into().unwrap()), &size)?; |
244 | if success { |
245 | Scalar::from_i32(1) // return TRUE |
246 | } else { |
247 | this.set_last_error(this.eval_windows("c" , "ERROR_INSUFFICIENT_BUFFER" ))?; |
248 | Scalar::from_i32(0) // return FALSE |
249 | } |
250 | } |
251 | None => { |
252 | // We have to pick some error code. |
253 | this.set_last_error(this.eval_windows("c" , "ERROR_BAD_USER_PROFILE" ))?; |
254 | Scalar::from_i32(0) // return FALSE |
255 | } |
256 | }) |
257 | } |
258 | } |
259 | |