1
1
use std:: ffi:: { OsString , OsStr } ;
2
2
use std:: env;
3
3
use std:: convert:: TryFrom ;
4
+ use std:: collections:: hash_map:: Values ;
4
5
5
6
use crate :: stacked_borrows:: Tag ;
6
7
use crate :: rustc_target:: abi:: LayoutOf ;
@@ -36,6 +37,10 @@ impl<'tcx> EnvVars<'tcx> {
36
37
}
37
38
ecx. update_environ ( )
38
39
}
40
+
41
+ pub ( super ) fn values ( & self ) -> InterpResult < ' tcx , Values < ' _ , OsString , Pointer < Tag > > > {
42
+ Ok ( self . map . values ( ) )
43
+ }
39
44
}
40
45
41
46
fn alloc_env_var_as_target_str < ' mir , ' tcx > (
@@ -54,33 +59,109 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
54
59
fn getenv ( & mut self , name_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , Scalar < Tag > > {
55
60
let this = self . eval_context_mut ( ) ;
56
61
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" ) ;
58
63
59
64
let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
60
65
let name = this. read_os_str_from_c_str ( name_ptr) ?;
61
66
Ok ( match this. machine . env_vars . map . get ( name) {
62
- // The offset is used to strip the "{name}=" part of the string.
63
67
Some ( var_ptr) => {
68
+ // The offset is used to strip the "{name}=" part of the string.
64
69
Scalar :: from ( var_ptr. offset ( Size :: from_bytes ( u64:: try_from ( name. len ( ) ) . unwrap ( ) . checked_add ( 1 ) . unwrap ( ) ) , this) ?)
65
70
}
66
71
None => Scalar :: ptr_null ( & * this. tcx ) ,
67
72
} )
68
73
}
69
74
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
+
70
148
fn setenv (
71
149
& mut self ,
72
150
name_op : OpTy < ' tcx , Tag > ,
73
151
value_op : OpTy < ' tcx , Tag > ,
74
152
) -> InterpResult < ' tcx , i32 > {
75
153
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" ) ;
76
156
77
157
let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
78
158
let value_ptr = this. read_scalar ( value_op) ?. not_undef ( ) ?;
79
- let value = this . read_os_str_from_target_str ( value_ptr ) ? ;
159
+
80
160
let mut new = None ;
81
161
if !this. is_null ( name_ptr) ? {
82
162
let name = this. read_os_str_from_target_str ( name_ptr) ?;
83
163
if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
164
+ let value = this. read_os_str_from_target_str ( value_ptr) ?;
84
165
new = Some ( ( name. to_owned ( ) , value. to_owned ( ) ) ) ;
85
166
}
86
167
}
@@ -91,19 +172,59 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
91
172
. deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
92
173
}
93
174
this. update_environ ( ) ?;
94
- Ok ( 0 )
175
+ Ok ( 0 ) // return zero on success
95
176
} else {
96
177
Ok ( -1 )
97
178
}
98
179
}
99
180
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
+
100
221
fn unsetenv ( & mut self , name_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
101
222
let this = self . eval_context_mut ( ) ;
102
223
103
224
let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
104
225
let mut success = None ;
105
226
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 ( ) ;
107
228
if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
108
229
success = Some ( this. machine . env_vars . map . remove ( & name) ) ;
109
230
}
0 commit comments