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 ;
@@ -36,6 +37,10 @@ impl<'tcx> EnvVars<'tcx> {
3637 }
3738 ecx. update_environ ( )
3839 }
40+
41+ pub ( super ) fn values ( & self ) -> InterpResult < ' tcx , Values < ' _ , OsString , Pointer < Tag > > > {
42+ Ok ( self . map . values ( ) )
43+ }
3944}
4045
4146fn alloc_env_var_as_target_str < ' mir , ' tcx > (
@@ -54,33 +59,109 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
5459 fn getenv ( & mut self , name_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , Scalar < Tag > > {
5560 let this = self . eval_context_mut ( ) ;
5661 let target_os = this. tcx . sess . target . target . target_os . as_str ( ) ;
57- assert ! ( target_os == "linux" || target_os == "macos" , "`{} ` is only available for the UNIX target family" ) ;
62+ assert ! ( target_os == "linux" || target_os == "macos" , "`getenv ` is only available for the UNIX target family" ) ;
5863
5964 let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
6065 let name = this. read_os_str_from_c_str ( name_ptr) ?;
6166 Ok ( match this. machine . env_vars . map . get ( name) {
62- // The offset is used to strip the "{name}=" part of the string.
6367 Some ( var_ptr) => {
68+ // The offset is used to strip the "{name}=" part of the string.
6469 Scalar :: from ( var_ptr. offset ( Size :: from_bytes ( u64:: try_from ( name. len ( ) ) . unwrap ( ) . checked_add ( 1 ) . unwrap ( ) ) , this) ?)
6570 }
6671 None => Scalar :: ptr_null ( & * this. tcx ) ,
6772 } )
6873 }
6974
75+ fn getenvironmentvariablew (
76+ & mut self ,
77+ name_op : OpTy < ' tcx , Tag > , // LPCWSTR lpName
78+ buf_op : OpTy < ' tcx , Tag > , // LPWSTR lpBuffer
79+ size_op : OpTy < ' tcx , Tag > , // DWORD nSize
80+ ) -> InterpResult < ' tcx , u64 > {
81+ let this = self . eval_context_mut ( ) ;
82+ this. assert_target_os ( "windows" , "GetEnvironmentVariableW" ) ;
83+
84+ let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
85+ let name = this. read_os_str_from_wide_str ( name_ptr) ?;
86+ Ok ( match this. machine . env_vars . map . get ( & name) {
87+ Some ( var_ptr) => {
88+ // The offset is used to strip the "{name}=" part of the string.
89+ let name_offset_bytes =
90+ u64:: try_from ( name. len ( ) ) . unwrap ( ) . checked_add ( 1 ) . unwrap ( ) . checked_mul ( 2 ) . unwrap ( ) ;
91+ let var_ptr = Scalar :: from ( var_ptr. offset ( Size :: from_bytes ( name_offset_bytes) , this) ?) ;
92+
93+ let var_size = u64:: try_from ( this. read_os_str_from_wide_str ( var_ptr) ?. len ( ) ) . unwrap ( ) ;
94+ let buf_size = u64:: try_from ( this. read_scalar ( size_op) ?. to_i32 ( ) ?) . unwrap ( ) ;
95+ let return_val = if var_size. checked_add ( 1 ) . unwrap ( ) > buf_size {
96+ // If lpBuffer is not large enough to hold the data, the return value is the buffer size, in characters,
97+ // required to hold the string and its terminating null character and the contents of lpBuffer are undefined.
98+ var_size + 1
99+ } else {
100+ let buf_ptr = this. read_scalar ( buf_op) ?. not_undef ( ) ?;
101+ for i in 0 ..var_size {
102+ this. memory . copy (
103+ this. force_ptr ( var_ptr. ptr_offset ( Size :: from_bytes ( i) * 2 , this) ?) ?,
104+ this. force_ptr ( buf_ptr. ptr_offset ( Size :: from_bytes ( i) * 2 , this) ?) ?,
105+ Size :: from_bytes ( 2 ) ,
106+ true ,
107+ ) ?;
108+ }
109+ // If the function succeeds, the return value is the number of characters stored in the buffer pointed to by lpBuffer,
110+ // not including the terminating null character.
111+ var_size
112+ } ;
113+ return_val
114+ }
115+ None => {
116+ this. set_last_error ( Scalar :: from_u32 ( 203 ) ) ?; // ERROR_ENVVAR_NOT_FOUND
117+ 0 // return zero upon failure
118+ }
119+ } )
120+ }
121+
122+ fn getenvironmentstringsw ( & mut self ) -> InterpResult < ' tcx , Scalar < Tag > > {
123+ let this = self . eval_context_mut ( ) ;
124+ this. assert_target_os ( "windows" , "GetEnvironmentStringsW" ) ;
125+
126+ // Info on layout of environment blocks in Windows:
127+ // https://docs.microsoft.com/en-us/windows/win32/procthread/environment-variables
128+ let mut env_vars = std:: ffi:: OsString :: new ( ) ;
129+ for & item in this. machine . env_vars . values ( ) ? {
130+ let env_var = this. read_os_str_from_target_str ( Scalar :: from ( item) ) ?;
131+ env_vars. push ( env_var) ;
132+ env_vars. push ( "\0 " ) ;
133+ }
134+
135+ // Allocate environment block
136+ let tcx = this. tcx ;
137+ let env_block_size = env_vars. len ( ) . checked_add ( 1 ) . unwrap ( ) ;
138+ let env_block_type = tcx. mk_array ( tcx. types . u16 , u64:: try_from ( env_block_size) . unwrap ( ) ) ;
139+ let env_block_place = this. allocate ( this. layout_of ( env_block_type) ?, MiriMemoryKind :: WinHeap . into ( ) ) ;
140+
141+ // Store environment variables to environment block
142+ // Final null terminator(block terminator) is pushed by `write_os_str_to_wide_str`
143+ this. write_os_str_to_wide_str ( & env_vars, env_block_place, u64:: try_from ( env_block_size) . unwrap ( ) ) ?;
144+
145+ Ok ( env_block_place. ptr )
146+ }
147+
70148 fn setenv (
71149 & mut self ,
72150 name_op : OpTy < ' tcx , Tag > ,
73151 value_op : OpTy < ' tcx , Tag > ,
74152 ) -> InterpResult < ' tcx , i32 > {
75153 let mut this = self . eval_context_mut ( ) ;
154+ let target_os = this. tcx . sess . target . target . target_os . as_str ( ) ;
155+ assert ! ( target_os == "linux" || target_os == "macos" , "`setenv` is only available for the UNIX target family" ) ;
76156
77157 let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
78158 let value_ptr = this. read_scalar ( value_op) ?. not_undef ( ) ?;
79- let value = this . read_os_str_from_target_str ( value_ptr ) ? ;
159+
80160 let mut new = None ;
81161 if !this. is_null ( name_ptr) ? {
82162 let name = this. read_os_str_from_target_str ( name_ptr) ?;
83163 if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
164+ let value = this. read_os_str_from_target_str ( value_ptr) ?;
84165 new = Some ( ( name. to_owned ( ) , value. to_owned ( ) ) ) ;
85166 }
86167 }
@@ -91,19 +172,59 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
91172 . deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
92173 }
93174 this. update_environ ( ) ?;
94- Ok ( 0 )
175+ Ok ( 0 ) // return zero on success
95176 } else {
96177 Ok ( -1 )
97178 }
98179 }
99180
181+ fn setenvironmentvariablew (
182+ & mut self ,
183+ name_op : OpTy < ' tcx , Tag > , // LPCWSTR lpName,
184+ value_op : OpTy < ' tcx , Tag > , // LPCWSTR lpValue,
185+ ) -> InterpResult < ' tcx , i32 > {
186+ let mut this = self . eval_context_mut ( ) ;
187+ this. assert_target_os ( "windows" , "SetEnvironmentVariableW" ) ;
188+
189+ let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
190+ let value_ptr = this. read_scalar ( value_op) ?. not_undef ( ) ?;
191+
192+ let mut new = None ;
193+ if !this. is_null ( name_ptr) ? {
194+ let name = this. read_os_str_from_target_str ( name_ptr) ?;
195+ if this. is_null ( value_ptr) ? {
196+ // Delete environment variable `{name}`
197+ if let Some ( var) = this. machine . env_vars . map . remove ( & name) {
198+ this. memory . deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
199+ this. update_environ ( ) ?;
200+ }
201+ return Ok ( 1 ) ; // return non-zero on success
202+ }
203+ if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
204+ let value = this. read_os_str_from_target_str ( value_ptr) ?;
205+ new = Some ( ( name. to_owned ( ) , value. to_owned ( ) ) ) ;
206+ }
207+ }
208+ if let Some ( ( name, value) ) = new {
209+ let var_ptr = alloc_env_var_as_target_str ( & name, & value, & mut this) ?;
210+ if let Some ( var) = this. machine . env_vars . map . insert ( name, var_ptr) {
211+ this. memory
212+ . deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
213+ }
214+ this. update_environ ( ) ?;
215+ Ok ( 1 ) // return non-zero on success
216+ } else {
217+ Ok ( 0 )
218+ }
219+ }
220+
100221 fn unsetenv ( & mut self , name_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
101222 let this = self . eval_context_mut ( ) ;
102223
103224 let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
104225 let mut success = None ;
105226 if !this. is_null ( name_ptr) ? {
106- let name = this. read_os_str_from_c_str ( name_ptr) ?. to_owned ( ) ;
227+ let name = this. read_os_str_from_target_str ( name_ptr) ?. to_owned ( ) ;
107228 if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
108229 success = Some ( this. machine . env_vars . map . remove ( & name) ) ;
109230 }
0 commit comments