@@ -23,10 +23,16 @@ pub struct EnvVars<'tcx> {
2323impl < ' tcx > EnvVars < ' tcx > {
2424 pub ( crate ) fn init < ' mir > (
2525 ecx : & mut InterpCx < ' mir , ' tcx , Evaluator < ' tcx > > ,
26- excluded_env_vars : Vec < String > ,
26+ mut excluded_env_vars : Vec < String > ,
2727 ) -> 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+
2835 if ecx. machine . communicate {
29- let target_os = ecx. tcx . sess . target . target . target_os . as_str ( ) ;
3036 for ( name, value) in env:: vars ( ) {
3137 if !excluded_env_vars. contains ( & name) {
3238 let var_ptr = match target_os {
@@ -82,6 +88,82 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
8288 } )
8389 }
8490
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+
85167 fn setenv (
86168 & mut self ,
87169 name_op : OpTy < ' tcx , Tag > ,
@@ -118,6 +200,47 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
118200 }
119201 }
120202
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+
121244 fn unsetenv ( & mut self , name_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
122245 let this = self . eval_context_mut ( ) ;
123246 let target_os = & this. tcx . sess . target . target . target_os ;
0 commit comments