1
+ #![ allow( non_snake_case) ]
2
+
1
3
use std:: ffi:: { OsString , OsStr } ;
2
4
use std:: env;
3
5
use std:: convert:: TryFrom ;
@@ -26,7 +28,7 @@ impl<'tcx> EnvVars<'tcx> {
26
28
ecx : & mut InterpCx < ' mir , ' tcx , Evaluator < ' tcx > > ,
27
29
mut excluded_env_vars : Vec < String > ,
28
30
) -> InterpResult < ' tcx > {
29
- if ecx. tcx . sess . target . target . target_os . to_lowercase ( ) == "windows" {
31
+ if ecx. tcx . sess . target . target . target_os == "windows" {
30
32
// Exclude `TERM` var to avoid terminfo trying to open the termcap file.
31
33
excluded_env_vars. push ( "TERM" . to_owned ( ) ) ;
32
34
}
@@ -46,7 +48,7 @@ impl<'tcx> EnvVars<'tcx> {
46
48
ecx. update_environ ( )
47
49
}
48
50
49
- pub ( super ) fn values ( & self ) -> InterpResult < ' tcx , Values < ' _ , OsString , Pointer < Tag > > > {
51
+ fn values ( & self ) -> InterpResult < ' tcx , Values < ' _ , OsString , Pointer < Tag > > > {
50
52
Ok ( self . map . values ( ) )
51
53
}
52
54
}
@@ -73,6 +75,28 @@ fn alloc_env_var_as_wide_str<'mir, 'tcx>(
73
75
Ok ( ecx. alloc_os_str_as_wide_str ( name_osstring. as_os_str ( ) , MiriMemoryKind :: Machine . into ( ) ) )
74
76
}
75
77
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
+
76
100
impl < ' mir , ' tcx > EvalContextExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
77
101
pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriEvalContextExt < ' mir , ' tcx > {
78
102
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
91
115
} )
92
116
}
93
117
94
- fn getenvironmentvariablew (
118
+ fn GetEnvironmentVariableW (
95
119
& mut self ,
96
120
name_op : OpTy < ' tcx , Tag > , // LPCWSTR lpName
97
121
buf_op : OpTy < ' tcx , Tag > , // LPWSTR lpBuffer
@@ -110,21 +134,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
110
134
let var_ptr = Scalar :: from ( var_ptr. offset ( Size :: from_bytes ( name_offset_bytes) , this) ?) ;
111
135
112
136
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 ( ) ;
114
139
let return_val = if var_size. checked_add ( 1 ) . unwrap ( ) > buf_size {
115
140
// If lpBuffer is not large enough to hold the data, the return value is the buffer size, in characters,
116
141
// required to hold the string and its terminating null character and the contents of lpBuffer are undefined.
117
142
var_size + 1
118
143
} else {
119
144
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 ) ?;
128
147
// If the function succeeds, the return value is the number of characters stored in the buffer pointed to by lpBuffer,
129
148
// not including the terminating null character.
130
149
var_size
@@ -138,30 +157,31 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
138
157
} )
139
158
}
140
159
141
- fn getenvironmentstringsw ( & mut self ) -> InterpResult < ' tcx , Scalar < Tag > > {
160
+ fn GetEnvironmentStringsW ( & mut self ) -> InterpResult < ' tcx , Scalar < Tag > > {
142
161
let this = self . eval_context_mut ( ) ;
143
162
this. assert_target_os ( "windows" , "GetEnvironmentStringsW" ) ;
144
163
145
164
// Info on layout of environment blocks in Windows:
146
165
// https://docs.microsoft.com/en-us/windows/win32/procthread/environment-variables
147
166
let mut env_vars = std:: ffi:: OsString :: new ( ) ;
148
167
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) ) ?;
150
169
env_vars. push ( env_var) ;
151
170
env_vars. push ( "\0 " ) ;
152
171
}
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 ( ) ) ;
153
175
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" ) ;
163
182
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 ( ) )
165
185
}
166
186
167
187
fn setenv (
@@ -200,7 +220,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
200
220
}
201
221
}
202
222
203
- fn setenvironmentvariablew (
223
+ fn SetEnvironmentVariableW (
204
224
& mut self ,
205
225
name_op : OpTy < ' tcx , Tag > , // LPCWSTR lpName,
206
226
value_op : OpTy < ' tcx , Tag > , // LPCWSTR lpValue,
@@ -211,32 +231,32 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
211
231
let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
212
232
let value_ptr = this. read_scalar ( value_op) ?. not_undef ( ) ?;
213
233
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" ) ;
229
237
}
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) ?;
232
254
if let Some ( var) = this. machine . env_vars . map . insert ( name, var_ptr) {
233
255
this. memory
234
256
. deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
235
257
}
236
258
this. update_environ ( ) ?;
237
259
Ok ( 1 ) // return non-zero on success
238
- } else {
239
- Ok ( 0 )
240
260
}
241
261
}
242
262
@@ -248,7 +268,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
248
268
let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
249
269
let mut success = None ;
250
270
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 ( ) ;
252
272
if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
253
273
success = Some ( this. machine . env_vars . map . remove ( & name) ) ;
254
274
}
0 commit comments