11use std:: ffi:: { OsString , OsStr } ;
22use std:: env;
33use std:: convert:: TryFrom ;
4+ use std:: collections:: hash_map:: Values ;
45
56use crate :: stacked_borrows:: Tag ;
67use crate :: rustc_target:: abi:: LayoutOf ;
@@ -40,6 +41,10 @@ impl<'tcx> EnvVars<'tcx> {
4041 }
4142 ecx. update_environ ( )
4243 }
44+
45+ pub ( super ) fn values ( & self ) -> InterpResult < ' tcx , Values < ' _ , OsString , Pointer < Tag > > > {
46+ Ok ( self . map . values ( ) )
47+ }
4348}
4449
4550fn alloc_env_var_as_c_str < ' mir , ' tcx > (
@@ -82,6 +87,79 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
8287 } )
8388 }
8489
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+
85163 fn setenv (
86164 & mut self ,
87165 name_op : OpTy < ' tcx , Tag > ,
@@ -118,6 +196,46 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
118196 }
119197 }
120198
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+
121239 fn unsetenv ( & mut self , name_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
122240 let this = self . eval_context_mut ( ) ;
123241 let target_os = & this. tcx . sess . target . target . target_os ;
@@ -126,7 +244,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
126244 let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
127245 let mut success = None ;
128246 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 ( ) ;
130248 if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
131249 success = Some ( this. machine . env_vars . map . remove ( & name) ) ;
132250 }
0 commit comments