1+ #![ allow( non_snake_case) ]
2+
13use std:: ffi:: { OsString , OsStr } ;
24use std:: env;
35use std:: convert:: TryFrom ;
@@ -26,7 +28,7 @@ impl<'tcx> EnvVars<'tcx> {
2628 ecx : & mut InterpCx < ' mir , ' tcx , Evaluator < ' tcx > > ,
2729 mut excluded_env_vars : Vec < String > ,
2830 ) -> InterpResult < ' tcx > {
29- if ecx. tcx . sess . target . target . target_os . to_lowercase ( ) == "windows" {
31+ if ecx. tcx . sess . target . target . target_os == "windows" {
3032 // Exclude `TERM` var to avoid terminfo trying to open the termcap file.
3133 excluded_env_vars. push ( "TERM" . to_owned ( ) ) ;
3234 }
@@ -46,7 +48,7 @@ impl<'tcx> EnvVars<'tcx> {
4648 ecx. update_environ ( )
4749 }
4850
49- pub ( super ) fn values ( & self ) -> InterpResult < ' tcx , Values < ' _ , OsString , Pointer < Tag > > > {
51+ fn values ( & self ) -> InterpResult < ' tcx , Values < ' _ , OsString , Pointer < Tag > > > {
5052 Ok ( self . map . values ( ) )
5153 }
5254}
@@ -73,6 +75,28 @@ fn alloc_env_var_as_wide_str<'mir, 'tcx>(
7375 Ok ( ecx. alloc_os_str_as_wide_str ( name_osstring. as_os_str ( ) , MiriMemoryKind :: Machine . into ( ) ) )
7476}
7577
78+ fn alloc_env_var_as_c_str < ' mir , ' tcx > (
79+ name : & OsStr ,
80+ value : & OsStr ,
81+ ecx : & mut InterpCx < ' mir , ' tcx , Evaluator < ' tcx > > ,
82+ ) -> InterpResult < ' tcx , Pointer < Tag > > {
83+ let mut name_osstring = name. to_os_string ( ) ;
84+ name_osstring. push ( "=" ) ;
85+ name_osstring. push ( value) ;
86+ Ok ( ecx. alloc_os_str_as_c_str ( name_osstring. as_os_str ( ) , MiriMemoryKind :: Machine . into ( ) ) )
87+ }
88+
89+ fn alloc_env_var_as_wide_str < ' mir , ' tcx > (
90+ name : & OsStr ,
91+ value : & OsStr ,
92+ ecx : & mut InterpCx < ' mir , ' tcx , Evaluator < ' tcx > > ,
93+ ) -> InterpResult < ' tcx , Pointer < Tag > > {
94+ let mut name_osstring = name. to_os_string ( ) ;
95+ name_osstring. push ( "=" ) ;
96+ name_osstring. push ( value) ;
97+ Ok ( ecx. alloc_os_str_as_wide_str ( name_osstring. as_os_str ( ) , MiriMemoryKind :: Machine . into ( ) ) )
98+ }
99+
76100impl < ' mir , ' tcx > EvalContextExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
77101pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriEvalContextExt < ' mir , ' tcx > {
78102 fn getenv ( & mut self , name_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , Scalar < Tag > > {
@@ -91,7 +115,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
91115 } )
92116 }
93117
94- fn getenvironmentvariablew (
118+ fn GetEnvironmentVariableW (
95119 & mut self ,
96120 name_op : OpTy < ' tcx , Tag > , // LPCWSTR lpName
97121 buf_op : OpTy < ' tcx , Tag > , // LPWSTR lpBuffer
@@ -110,21 +134,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
110134 let var_ptr = Scalar :: from ( var_ptr. offset ( Size :: from_bytes ( name_offset_bytes) , this) ?) ;
111135
112136 let var_size = u64:: try_from ( this. read_os_str_from_wide_str ( var_ptr) ?. len ( ) ) . unwrap ( ) ;
113- let buf_size = u64:: try_from ( this. read_scalar ( size_op) ?. to_i32 ( ) ?) . unwrap ( ) ;
137+ // `buf_size` represent size in characters.
138+ let buf_size = u64:: try_from ( this. read_scalar ( size_op) ?. to_u32 ( ) ?) . unwrap ( ) ;
114139 let return_val = if var_size. checked_add ( 1 ) . unwrap ( ) > buf_size {
115140 // If lpBuffer is not large enough to hold the data, the return value is the buffer size, in characters,
116141 // required to hold the string and its terminating null character and the contents of lpBuffer are undefined.
117142 var_size + 1
118143 } else {
119144 let buf_ptr = this. read_scalar ( buf_op) ?. not_undef ( ) ?;
120- for i in 0 ..var_size {
121- this. memory . copy (
122- this. force_ptr ( var_ptr. ptr_offset ( Size :: from_bytes ( i) * 2 , this) ?) ?,
123- this. force_ptr ( buf_ptr. ptr_offset ( Size :: from_bytes ( i) * 2 , this) ?) ?,
124- Size :: from_bytes ( 2 ) ,
125- true ,
126- ) ?;
127- }
145+ let bytes_to_be_copied = var_size. checked_add ( 1 ) . unwrap ( ) . checked_mul ( 2 ) . unwrap ( ) ;
146+ this. memory . copy ( this. force_ptr ( var_ptr) ?, this. force_ptr ( buf_ptr) ?, Size :: from_bytes ( bytes_to_be_copied) , true ) ?;
128147 // If the function succeeds, the return value is the number of characters stored in the buffer pointed to by lpBuffer,
129148 // not including the terminating null character.
130149 var_size
@@ -138,30 +157,31 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
138157 } )
139158 }
140159
141- fn getenvironmentstringsw ( & mut self ) -> InterpResult < ' tcx , Scalar < Tag > > {
160+ fn GetEnvironmentStringsW ( & mut self ) -> InterpResult < ' tcx , Scalar < Tag > > {
142161 let this = self . eval_context_mut ( ) ;
143162 this. assert_target_os ( "windows" , "GetEnvironmentStringsW" ) ;
144163
145164 // Info on layout of environment blocks in Windows:
146165 // https://docs.microsoft.com/en-us/windows/win32/procthread/environment-variables
147166 let mut env_vars = std:: ffi:: OsString :: new ( ) ;
148167 for & item in this. machine . env_vars . values ( ) ? {
149- let env_var = this. read_os_str_from_target_str ( Scalar :: from ( item) ) ?;
168+ let env_var = this. read_os_str_from_wide_str ( Scalar :: from ( item) ) ?;
150169 env_vars. push ( env_var) ;
151170 env_vars. push ( "\0 " ) ;
152171 }
172+ // Allocate environment block & Store environment variables to environment block.
173+ // Final null terminator(block terminator) is added by `alloc_os_str_to_wide_str`.
174+ let envblock_ptr = this. alloc_os_str_as_wide_str ( & env_vars, MiriMemoryKind :: WinHeap . into ( ) ) ;
153175
154- // Allocate environment block
155- let tcx = this. tcx ;
156- let env_block_size = env_vars. len ( ) . checked_add ( 1 ) . unwrap ( ) ;
157- let env_block_type = tcx. mk_array ( tcx. types . u16 , u64:: try_from ( env_block_size) . unwrap ( ) ) ;
158- let env_block_place = this. allocate ( this. layout_of ( env_block_type) ?, MiriMemoryKind :: WinHeap . into ( ) ) ;
159-
160- // Store environment variables to environment block
161- // Final null terminator(block terminator) is pushed by `write_os_str_to_wide_str`
162- this. write_os_str_to_wide_str ( & env_vars, env_block_place, u64:: try_from ( env_block_size) . unwrap ( ) ) ?;
176+ Ok ( envblock_ptr. into ( ) )
177+ }
178+
179+ fn FreeEnvironmentStringsW ( & mut self , env_block_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , bool > {
180+ let this = self . eval_context_mut ( ) ;
181+ this. assert_target_os ( "windows" , "FreeEnvironmentStringsW" ) ;
163182
164- Ok ( env_block_place. ptr )
183+ let env_block_ptr = this. read_scalar ( env_block_op) ?. not_undef ( ) ?;
184+ Ok ( this. memory . deallocate ( this. force_ptr ( env_block_ptr) ?, None , MiriMemoryKind :: WinHeap . into ( ) ) . is_ok ( ) )
165185 }
166186
167187 fn setenv (
@@ -200,7 +220,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
200220 }
201221 }
202222
203- fn setenvironmentvariablew (
223+ fn SetEnvironmentVariableW (
204224 & mut self ,
205225 name_op : OpTy < ' tcx , Tag > , // LPCWSTR lpName,
206226 value_op : OpTy < ' tcx , Tag > , // LPCWSTR lpValue,
@@ -211,32 +231,32 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
211231 let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
212232 let value_ptr = this. read_scalar ( value_op) ?. not_undef ( ) ?;
213233
214- let mut new = None ;
215- if !this. is_null ( name_ptr) ? {
216- let name = this. read_os_str_from_target_str ( name_ptr) ?;
217- if this. is_null ( value_ptr) ? {
218- // Delete environment variable `{name}`
219- if let Some ( var) = this. machine . env_vars . map . remove ( & name) {
220- this. memory . deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
221- this. update_environ ( ) ?;
222- }
223- return Ok ( 1 ) ; // return non-zero on success
224- }
225- if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
226- let value = this. read_os_str_from_target_str ( value_ptr) ?;
227- new = Some ( ( name. to_owned ( ) , value. to_owned ( ) ) ) ;
228- }
234+ if this. is_null ( name_ptr) ? {
235+ // ERROR CODE is not clearly explained in docs.. For now, throw UB instead.
236+ throw_ub_format ! ( "Pointer to environment variable name is NULL" ) ;
229237 }
230- if let Some ( ( name, value) ) = new {
231- let var_ptr = alloc_env_var_as_target_str ( & name, & value, & mut this) ?;
238+
239+ let name = this. read_os_str_from_wide_str ( name_ptr) ?;
240+ if name. is_empty ( ) {
241+ throw_unsup_format ! ( "Environment variable name is an empty string" ) ;
242+ } else if name. to_string_lossy ( ) . contains ( '=' ) {
243+ throw_unsup_format ! ( "Environment variable name contains '='" ) ;
244+ } else if this. is_null ( value_ptr) ? {
245+ // Delete environment variable `{name}`
246+ if let Some ( var) = this. machine . env_vars . map . remove ( & name) {
247+ this. memory . deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
248+ this. update_environ ( ) ?;
249+ }
250+ Ok ( 1 ) // return non-zero on success
251+ } else {
252+ let value = this. read_os_str_from_wide_str ( value_ptr) ?;
253+ let var_ptr = alloc_env_var_as_wide_str ( & name, & value, & mut this) ?;
232254 if let Some ( var) = this. machine . env_vars . map . insert ( name, var_ptr) {
233255 this. memory
234256 . deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
235257 }
236258 this. update_environ ( ) ?;
237259 Ok ( 1 ) // return non-zero on success
238- } else {
239- Ok ( 0 )
240260 }
241261 }
242262
@@ -248,7 +268,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
248268 let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
249269 let mut success = None ;
250270 if !this. is_null ( name_ptr) ? {
251- let name = this. read_os_str_from_target_str ( name_ptr) ?. to_owned ( ) ;
271+ let name = this. read_os_str_from_c_str ( name_ptr) ?. to_owned ( ) ;
252272 if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
253273 success = Some ( this. machine . env_vars . map . remove ( & name) ) ;
254274 }
0 commit comments