1
1
use std:: ffi:: { OsString , OsStr } ;
2
2
use std:: env;
3
3
use std:: convert:: TryFrom ;
4
+ use std:: collections:: hash_map:: Values ;
4
5
5
6
use crate :: stacked_borrows:: Tag ;
6
7
use crate :: rustc_target:: abi:: LayoutOf ;
@@ -40,6 +41,10 @@ impl<'tcx> EnvVars<'tcx> {
40
41
}
41
42
ecx. update_environ ( )
42
43
}
44
+
45
+ pub ( super ) fn values ( & self ) -> InterpResult < ' tcx , Values < ' _ , OsString , Pointer < Tag > > > {
46
+ Ok ( self . map . values ( ) )
47
+ }
43
48
}
44
49
45
50
fn alloc_env_var_as_c_str < ' mir , ' tcx > (
@@ -82,6 +87,79 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
82
87
} )
83
88
}
84
89
90
+ fn getenvironmentvariablew (
91
+ & mut self ,
92
+ name_op : OpTy < ' tcx , Tag > , // LPCWSTR lpName
93
+ buf_op : OpTy < ' tcx , Tag > , // LPWSTR lpBuffer
94
+ size_op : OpTy < ' tcx , Tag > , // DWORD nSize
95
+ ) -> InterpResult < ' tcx , u64 > {
96
+ let this = self . eval_context_mut ( ) ;
97
+ this. assert_target_os ( "windows" , "GetEnvironmentVariableW" ) ;
98
+
99
+ let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
100
+ let name = this. read_os_str_from_wide_str ( name_ptr) ?;
101
+ Ok ( match this. machine . env_vars . map . get ( & name) {
102
+ Some ( var_ptr) => {
103
+ // The offset is used to strip the "{name}=" part of the string.
104
+ let name_offset_bytes =
105
+ u64:: try_from ( name. len ( ) ) . unwrap ( ) . checked_add ( 1 ) . unwrap ( ) . checked_mul ( 2 ) . unwrap ( ) ;
106
+ let var_ptr = Scalar :: from ( var_ptr. offset ( Size :: from_bytes ( name_offset_bytes) , this) ?) ;
107
+
108
+ let var_size = u64:: try_from ( this. read_os_str_from_wide_str ( var_ptr) ?. len ( ) ) . unwrap ( ) ;
109
+ let buf_size = u64:: try_from ( this. read_scalar ( size_op) ?. to_i32 ( ) ?) . unwrap ( ) ;
110
+ let return_val = if var_size. checked_add ( 1 ) . unwrap ( ) > buf_size {
111
+ // If lpBuffer is not large enough to hold the data, the return value is the buffer size, in characters,
112
+ // required to hold the string and its terminating null character and the contents of lpBuffer are undefined.
113
+ var_size + 1
114
+ } else {
115
+ let buf_ptr = this. read_scalar ( buf_op) ?. not_undef ( ) ?;
116
+ for i in 0 ..var_size {
117
+ this. memory . copy (
118
+ this. force_ptr ( var_ptr. ptr_offset ( Size :: from_bytes ( i) * 2 , this) ?) ?,
119
+ this. force_ptr ( buf_ptr. ptr_offset ( Size :: from_bytes ( i) * 2 , this) ?) ?,
120
+ Size :: from_bytes ( 2 ) ,
121
+ true ,
122
+ ) ?;
123
+ }
124
+ // If the function succeeds, the return value is the number of characters stored in the buffer pointed to by lpBuffer,
125
+ // not including the terminating null character.
126
+ var_size
127
+ } ;
128
+ return_val
129
+ }
130
+ None => {
131
+ this. set_last_error ( Scalar :: from_u32 ( 203 ) ) ?; // ERROR_ENVVAR_NOT_FOUND
132
+ 0 // return zero upon failure
133
+ }
134
+ } )
135
+ }
136
+
137
+ fn getenvironmentstringsw ( & mut self ) -> InterpResult < ' tcx , Scalar < Tag > > {
138
+ let this = self . eval_context_mut ( ) ;
139
+ this. assert_target_os ( "windows" , "GetEnvironmentStringsW" ) ;
140
+
141
+ // Info on layout of environment blocks in Windows:
142
+ // https://docs.microsoft.com/en-us/windows/win32/procthread/environment-variables
143
+ let mut env_vars = std:: ffi:: OsString :: new ( ) ;
144
+ for & item in this. machine . env_vars . values ( ) ? {
145
+ let env_var = this. read_os_str_from_target_str ( Scalar :: from ( item) ) ?;
146
+ env_vars. push ( env_var) ;
147
+ env_vars. push ( "\0 " ) ;
148
+ }
149
+
150
+ // Allocate environment block
151
+ let tcx = this. tcx ;
152
+ let env_block_size = env_vars. len ( ) . checked_add ( 1 ) . unwrap ( ) ;
153
+ let env_block_type = tcx. mk_array ( tcx. types . u16 , u64:: try_from ( env_block_size) . unwrap ( ) ) ;
154
+ let env_block_place = this. allocate ( this. layout_of ( env_block_type) ?, MiriMemoryKind :: WinHeap . into ( ) ) ;
155
+
156
+ // Store environment variables to environment block
157
+ // Final null terminator(block terminator) is pushed by `write_os_str_to_wide_str`
158
+ this. write_os_str_to_wide_str ( & env_vars, env_block_place, u64:: try_from ( env_block_size) . unwrap ( ) ) ?;
159
+
160
+ Ok ( env_block_place. ptr )
161
+ }
162
+
85
163
fn setenv (
86
164
& mut self ,
87
165
name_op : OpTy < ' tcx , Tag > ,
@@ -118,6 +196,46 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
118
196
}
119
197
}
120
198
199
+ fn setenvironmentvariablew (
200
+ & mut self ,
201
+ name_op : OpTy < ' tcx , Tag > , // LPCWSTR lpName,
202
+ value_op : OpTy < ' tcx , Tag > , // LPCWSTR lpValue,
203
+ ) -> InterpResult < ' tcx , i32 > {
204
+ let mut this = self . eval_context_mut ( ) ;
205
+ this. assert_target_os ( "windows" , "SetEnvironmentVariableW" ) ;
206
+
207
+ let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
208
+ let value_ptr = this. read_scalar ( value_op) ?. not_undef ( ) ?;
209
+
210
+ let mut new = None ;
211
+ if !this. is_null ( name_ptr) ? {
212
+ let name = this. read_os_str_from_target_str ( name_ptr) ?;
213
+ if this. is_null ( value_ptr) ? {
214
+ // Delete environment variable `{name}`
215
+ if let Some ( var) = this. machine . env_vars . map . remove ( & name) {
216
+ this. memory . deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
217
+ this. update_environ ( ) ?;
218
+ }
219
+ return Ok ( 1 ) ; // return non-zero on success
220
+ }
221
+ if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
222
+ let value = this. read_os_str_from_target_str ( value_ptr) ?;
223
+ new = Some ( ( name. to_owned ( ) , value. to_owned ( ) ) ) ;
224
+ }
225
+ }
226
+ if let Some ( ( name, value) ) = new {
227
+ let var_ptr = alloc_env_var_as_target_str ( & name, & value, & mut this) ?;
228
+ if let Some ( var) = this. machine . env_vars . map . insert ( name, var_ptr) {
229
+ this. memory
230
+ . deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
231
+ }
232
+ this. update_environ ( ) ?;
233
+ Ok ( 1 ) // return non-zero on success
234
+ } else {
235
+ Ok ( 0 )
236
+ }
237
+ }
238
+
121
239
fn unsetenv ( & mut self , name_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
122
240
let this = self . eval_context_mut ( ) ;
123
241
let target_os = & this. tcx . sess . target . target . target_os ;
@@ -126,7 +244,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
126
244
let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
127
245
let mut success = None ;
128
246
if !this. is_null ( name_ptr) ? {
129
- let name = this. read_os_str_from_c_str ( name_ptr) ?. to_owned ( ) ;
247
+ let name = this. read_os_str_from_target_str ( name_ptr) ?. to_owned ( ) ;
130
248
if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
131
249
success = Some ( this. machine . env_vars . map . remove ( & name) ) ;
132
250
}
0 commit comments