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 ;
@@ -13,7 +14,7 @@ use rustc_mir::interpret::Pointer;
1314#[ derive( Default ) ]
1415pub struct EnvVars < ' tcx > {
1516 /// Stores pointers to the environment variables. These variables must be stored as
16- /// null-terminated C strings with the `"{name}={value}"` format.
17+ /// null-terminated target strings(c_str or wide_str) with the `"{name}={value}"` format.
1718 map : FxHashMap < OsString , Pointer < Tag > > ,
1819
1920 /// Place where the `environ` static is stored. Lazily initialized, but then never changes.
@@ -29,42 +30,100 @@ impl<'tcx> EnvVars<'tcx> {
2930 for ( name, value) in env:: vars ( ) {
3031 if !excluded_env_vars. contains ( & name) {
3132 let var_ptr =
32- alloc_env_var_as_c_str ( name. as_ref ( ) , value. as_ref ( ) , ecx) ;
33+ alloc_env_var_as_target_str ( name. as_ref ( ) , value. as_ref ( ) , ecx) ? ;
3334 ecx. machine . env_vars . map . insert ( OsString :: from ( name) , var_ptr) ;
3435 }
3536 }
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
41- fn alloc_env_var_as_c_str < ' mir , ' tcx > (
46+ fn alloc_env_var_as_target_str < ' mir , ' tcx > (
4247 name : & OsStr ,
4348 value : & OsStr ,
4449 ecx : & mut InterpCx < ' mir , ' tcx , Evaluator < ' tcx > > ,
45- ) -> Pointer < Tag > {
50+ ) -> InterpResult < ' tcx , Pointer < Tag > > {
4651 let mut name_osstring = name. to_os_string ( ) ;
4752 name_osstring. push ( "=" ) ;
4853 name_osstring. push ( value) ;
49- ecx. alloc_os_str_as_c_str ( name_osstring. as_os_str ( ) , MiriMemoryKind :: Machine . into ( ) )
54+ Ok ( ecx. alloc_os_str_as_target_str ( name_osstring. as_os_str ( ) , MiriMemoryKind :: Machine . into ( ) ) ? )
5055}
5156
5257impl < ' mir , ' tcx > EvalContextExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
5358pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriEvalContextExt < ' mir , ' tcx > {
54- fn getenv ( & mut self , name_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , Scalar < Tag > > {
55- let this = self . eval_context_mut ( ) ;
59+ fn getenv ( & self , name_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , Scalar < Tag > > {
60+ let this = self . eval_context_ref ( ) ;
5661
5762 let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
58- let name = this. read_os_str_from_c_str ( name_ptr) ?;
59- Ok ( match this. machine . env_vars . map . get ( name) {
60- // The offset is used to strip the "{name}=" part of the string.
63+ let name = this. read_os_str_from_target_str ( name_ptr) ?;
64+ Ok ( match this. machine . env_vars . map . get ( & name) {
6165 Some ( var_ptr) => {
66+ // The offset is used to strip the "{name}=" part of the string.
6267 Scalar :: from ( var_ptr. offset ( Size :: from_bytes ( u64:: try_from ( name. len ( ) ) . unwrap ( ) . checked_add ( 1 ) . unwrap ( ) ) , this) ?)
6368 }
6469 None => Scalar :: ptr_null ( & * this. tcx ) ,
6570 } )
6671 }
6772
73+
74+ fn getenvironmentvariablew (
75+ & mut self ,
76+ name_op : OpTy < ' tcx , Tag > , // LPCWSTR lpName
77+ buf_op : OpTy < ' tcx , Tag > , // LPWSTR lpBuffer
78+ size_op : OpTy < ' tcx , Tag > , // DWORD nSize
79+ ) -> InterpResult < ' tcx , u32 > {
80+ let this = self . eval_context_mut ( ) ;
81+
82+ let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
83+ let name = this. read_os_str_from_target_str ( name_ptr) ?;
84+ Ok ( match this. machine . env_vars . map . get ( & name) {
85+ Some ( var_ptr) => {
86+ // The offset is used to strip the "{name}=" part of the string.
87+ let var_ptr = Scalar :: from ( var_ptr. offset ( Size :: from_bytes ( ( name. len ( ) as u64 + 1 ) * 2 ) , this) ?) ;
88+ let buf_size = this. read_scalar ( size_op) ?. to_i32 ( ) ? as u64 ;
89+ let buf_ptr = this. read_scalar ( buf_op) ?. not_undef ( ) ?;
90+ let size_u16 = Size :: from_bytes ( 2 ) ;
91+
92+ // The following loop attempts to figure out the length of env_var (`var_size`)
93+ let mut var_size = 0u64 ;
94+ loop {
95+ let temp_var_ptr = var_ptr. ptr_offset ( Size :: from_bytes ( var_size * 2 ) , this) ?;
96+ let bytes = this. memory . read_bytes ( temp_var_ptr, size_u16) ?;
97+ var_size += 1 ;
98+ // encountered 0x0000 terminator
99+ if bytes[ 0 ] == 0 && bytes[ 1 ] == 0 { break ; }
100+ }
101+
102+ let return_val = if var_size > buf_size {
103+ // If lpBuffer is not large enough to hold the data, the return value is the buffer size, in characters,
104+ // required to hold the string and its terminating null character and the contents of lpBuffer are undefined.
105+ var_size
106+ } else {
107+ for i in 0 ..var_size {
108+ this. memory . copy (
109+ this. force_ptr ( var_ptr. ptr_offset ( Size :: from_bytes ( i * 2 ) , this) ?) ?,
110+ this. force_ptr ( buf_ptr. ptr_offset ( Size :: from_bytes ( i * 2 ) , this) ?) ?,
111+ size_u16,
112+ true ,
113+ ) ?;
114+ }
115+ // If the function succeeds, the return value is the number of characters stored in the buffer pointed to by lpBuffer,
116+ // not including the terminating null character.
117+ var_size - 1
118+ } ;
119+ assert_eq ! ( return_val as u32 as u64 , return_val) ;
120+ return_val as u32
121+ }
122+ // return zero upon failure
123+ None => 0u32
124+ } )
125+ }
126+
68127 fn setenv (
69128 & mut self ,
70129 name_op : OpTy < ' tcx , Tag > ,
@@ -74,34 +133,43 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
74133
75134 let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
76135 let value_ptr = this. read_scalar ( value_op) ?. not_undef ( ) ?;
77- let value = this. read_os_str_from_c_str ( value_ptr) ?;
136+ let value = this. read_os_str_from_target_str ( value_ptr) ?;
78137 let mut new = None ;
79138 if !this. is_null ( name_ptr) ? {
80- let name = this. read_os_str_from_c_str ( name_ptr) ?;
139+ let name = this. read_os_str_from_target_str ( name_ptr) ?;
81140 if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
82141 new = Some ( ( name. to_owned ( ) , value. to_owned ( ) ) ) ;
83142 }
84143 }
85144 if let Some ( ( name, value) ) = new {
86- let var_ptr = alloc_env_var_as_c_str ( & name, & value, & mut this) ;
145+ let var_ptr = alloc_env_var_as_target_str ( & name, & value, & mut this) ? ;
87146 if let Some ( var) = this. machine . env_vars . map . insert ( name. to_owned ( ) , var_ptr) {
88147 this. memory
89148 . deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
90149 }
91150 this. update_environ ( ) ?;
92- Ok ( 0 )
151+ Ok ( 0 ) // return zero on success
93152 } else {
94153 Ok ( -1 )
95154 }
96155 }
97156
157+ fn setenvironmentvariablew (
158+ & mut self ,
159+ name_op : OpTy < ' tcx , Tag > , // LPCWSTR lpName,
160+ value_op : OpTy < ' tcx , Tag > , // LPCWSTR lpValue,
161+ ) -> InterpResult < ' tcx , i32 > {
162+ // return non-zero on success
163+ self . setenv ( name_op, value_op) . map ( |x| x + 1 )
164+ }
165+
98166 fn unsetenv ( & mut self , name_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
99167 let this = self . eval_context_mut ( ) ;
100168
101169 let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
102170 let mut success = None ;
103171 if !this. is_null ( name_ptr) ? {
104- let name = this. read_os_str_from_c_str ( name_ptr) ?. to_owned ( ) ;
172+ let name = this. read_os_str_from_target_str ( name_ptr) ?. to_owned ( ) ;
105173 if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
106174 success = Some ( this. machine . env_vars . map . remove ( & name) ) ;
107175 }
0 commit comments