@@ -23,10 +23,16 @@ pub struct EnvVars<'tcx> {
23
23
impl < ' tcx > EnvVars < ' tcx > {
24
24
pub ( crate ) fn init < ' mir > (
25
25
ecx : & mut InterpCx < ' mir , ' tcx , Evaluator < ' tcx > > ,
26
- excluded_env_vars : Vec < String > ,
26
+ mut excluded_env_vars : Vec < String > ,
27
27
) -> InterpResult < ' tcx > {
28
+ let target_os = ecx. tcx . sess . target . target . target_os . as_str ( ) ;
29
+ if target_os == "windows" {
30
+ // Temporary hack: Exclude `TERM` var to avoid terminfo trying to open the termcap file.
31
+ // Can be removed once https://github.com/rust-lang/miri/issues/1013 is resolved.
32
+ excluded_env_vars. push ( "TERM" . to_owned ( ) ) ;
33
+ }
34
+
28
35
if ecx. machine . communicate {
29
- let target_os = ecx. tcx . sess . target . target . target_os . as_str ( ) ;
30
36
for ( name, value) in env:: vars ( ) {
31
37
if !excluded_env_vars. contains ( & name) {
32
38
let var_ptr = match target_os {
@@ -82,6 +88,82 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
82
88
} )
83
89
}
84
90
91
+ #[ allow( non_snake_case) ]
92
+ fn GetEnvironmentVariableW (
93
+ & mut self ,
94
+ name_op : OpTy < ' tcx , Tag > , // LPCWSTR
95
+ buf_op : OpTy < ' tcx , Tag > , // LPWSTR
96
+ size_op : OpTy < ' tcx , Tag > , // DWORD
97
+ ) -> InterpResult < ' tcx , u64 > {
98
+ let this = self . eval_context_mut ( ) ;
99
+ this. assert_target_os ( "windows" , "GetEnvironmentVariableW" ) ;
100
+
101
+ let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
102
+ let name = this. read_os_str_from_wide_str ( name_ptr) ?;
103
+ Ok ( match this. machine . env_vars . map . get ( & name) {
104
+ Some ( var_ptr) => {
105
+ // The offset is used to strip the "{name}=" part of the string.
106
+ let name_offset_bytes =
107
+ u64:: try_from ( name. len ( ) ) . unwrap ( ) . checked_add ( 1 ) . unwrap ( ) . checked_mul ( 2 ) . unwrap ( ) ;
108
+ let var_ptr = Scalar :: from ( var_ptr. offset ( Size :: from_bytes ( name_offset_bytes) , this) ?) ;
109
+ let var = this. read_os_str_from_wide_str ( var_ptr) ?;
110
+
111
+ let buf_ptr = this. read_scalar ( buf_op) ?. not_undef ( ) ?;
112
+ // `buf_size` represents the size in characters.
113
+ let buf_size = u64:: try_from ( this. read_scalar ( size_op) ?. to_u32 ( ) ?) . unwrap ( ) ;
114
+ let ( success, len) = this. write_os_str_to_wide_str ( & var, buf_ptr, buf_size) ?;
115
+
116
+ if success {
117
+ // If the function succeeds, the return value is the number of characters stored in the buffer pointed to by lpBuffer,
118
+ // not including the terminating null character.
119
+ len
120
+ } else {
121
+ // If lpBuffer is not large enough to hold the data, the return value is the buffer size, in characters,
122
+ // required to hold the string and its terminating null character and the contents of lpBuffer are undefined.
123
+ len + 1
124
+ }
125
+ }
126
+ None => {
127
+ let envvar_not_found = this. eval_path_scalar ( & [ "std" , "sys" , "windows" , "c" , "ERROR_ENVVAR_NOT_FOUND" ] ) ?;
128
+ this. set_last_error ( envvar_not_found. not_undef ( ) ?) ?;
129
+ 0 // return zero upon failure
130
+ }
131
+ } )
132
+ }
133
+
134
+ #[ allow( non_snake_case) ]
135
+ fn GetEnvironmentStringsW ( & mut self ) -> InterpResult < ' tcx , Scalar < Tag > > {
136
+ let this = self . eval_context_mut ( ) ;
137
+ this. assert_target_os ( "windows" , "GetEnvironmentStringsW" ) ;
138
+
139
+ // Info on layout of environment blocks in Windows:
140
+ // https://docs.microsoft.com/en-us/windows/win32/procthread/environment-variables
141
+ let mut env_vars = std:: ffi:: OsString :: new ( ) ;
142
+ for & item in this. machine . env_vars . map . values ( ) {
143
+ let env_var = this. read_os_str_from_wide_str ( Scalar :: from ( item) ) ?;
144
+ env_vars. push ( env_var) ;
145
+ env_vars. push ( "\0 " ) ;
146
+ }
147
+ // Allocate environment block & Store environment variables to environment block.
148
+ // Final null terminator(block terminator) is added by `alloc_os_str_to_wide_str`.
149
+ // FIXME: MemoryKind should be `Machine`, blocked on https://github.com/rust-lang/rust/pull/70479.
150
+ let envblock_ptr = this. alloc_os_str_as_wide_str ( & env_vars, MiriMemoryKind :: WinHeap . into ( ) ) ;
151
+ // If the function succeeds, the return value is a pointer to the environment block of the current process.
152
+ Ok ( envblock_ptr. into ( ) )
153
+ }
154
+
155
+ #[ allow( non_snake_case) ]
156
+ fn FreeEnvironmentStringsW ( & mut self , env_block_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
157
+ let this = self . eval_context_mut ( ) ;
158
+ this. assert_target_os ( "windows" , "FreeEnvironmentStringsW" ) ;
159
+
160
+ let env_block_ptr = this. read_scalar ( env_block_op) ?. not_undef ( ) ?;
161
+ // FIXME: MemoryKind should be `Machine`, blocked on https://github.com/rust-lang/rust/pull/70479.
162
+ let result = this. memory . deallocate ( this. force_ptr ( env_block_ptr) ?, None , MiriMemoryKind :: WinHeap . into ( ) ) ;
163
+ // If the function succeeds, the return value is nonzero.
164
+ Ok ( result. is_ok ( ) as i32 )
165
+ }
166
+
85
167
fn setenv (
86
168
& mut self ,
87
169
name_op : OpTy < ' tcx , Tag > ,
@@ -118,6 +200,47 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
118
200
}
119
201
}
120
202
203
+ #[ allow( non_snake_case) ]
204
+ fn SetEnvironmentVariableW (
205
+ & mut self ,
206
+ name_op : OpTy < ' tcx , Tag > , // LPCWSTR
207
+ value_op : OpTy < ' tcx , Tag > , // LPCWSTR
208
+ ) -> InterpResult < ' tcx , i32 > {
209
+ let mut this = self . eval_context_mut ( ) ;
210
+ this. assert_target_os ( "windows" , "SetEnvironmentVariableW" ) ;
211
+
212
+ let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
213
+ let value_ptr = this. read_scalar ( value_op) ?. not_undef ( ) ?;
214
+
215
+ if this. is_null ( name_ptr) ? {
216
+ // ERROR CODE is not clearly explained in docs.. For now, throw UB instead.
217
+ throw_ub_format ! ( "pointer to environment variable name is NULL" ) ;
218
+ }
219
+
220
+ let name = this. read_os_str_from_wide_str ( name_ptr) ?;
221
+ if name. is_empty ( ) {
222
+ throw_unsup_format ! ( "environment variable name is an empty string" ) ;
223
+ } else if name. to_string_lossy ( ) . contains ( '=' ) {
224
+ throw_unsup_format ! ( "environment variable name contains '='" ) ;
225
+ } else if this. is_null ( value_ptr) ? {
226
+ // Delete environment variable `{name}`
227
+ if let Some ( var) = this. machine . env_vars . map . remove ( & name) {
228
+ this. memory . deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
229
+ this. update_environ ( ) ?;
230
+ }
231
+ Ok ( 1 ) // return non-zero on success
232
+ } else {
233
+ let value = this. read_os_str_from_wide_str ( value_ptr) ?;
234
+ let var_ptr = alloc_env_var_as_wide_str ( & name, & value, & mut this) ?;
235
+ if let Some ( var) = this. machine . env_vars . map . insert ( name, var_ptr) {
236
+ this. memory
237
+ . deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
238
+ }
239
+ this. update_environ ( ) ?;
240
+ Ok ( 1 ) // return non-zero on success
241
+ }
242
+ }
243
+
121
244
fn unsetenv ( & mut self , name_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
122
245
let this = self . eval_context_mut ( ) ;
123
246
let target_os = & this. tcx . sess . target . target . target_os ;
0 commit comments