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 ;
@@ -13,7 +14,7 @@ use rustc_mir::interpret::Pointer;
13
14
#[ derive( Default ) ]
14
15
pub struct EnvVars < ' tcx > {
15
16
/// 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.
17
18
map : FxHashMap < OsString , Pointer < Tag > > ,
18
19
19
20
/// Place where the `environ` static is stored. Lazily initialized, but then never changes.
@@ -29,42 +30,100 @@ impl<'tcx> EnvVars<'tcx> {
29
30
for ( name, value) in env:: vars ( ) {
30
31
if !excluded_env_vars. contains ( & name) {
31
32
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) ? ;
33
34
ecx. machine . env_vars . map . insert ( OsString :: from ( name) , var_ptr) ;
34
35
}
35
36
}
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
- fn alloc_env_var_as_c_str < ' mir , ' tcx > (
46
+ fn alloc_env_var_as_target_str < ' mir , ' tcx > (
42
47
name : & OsStr ,
43
48
value : & OsStr ,
44
49
ecx : & mut InterpCx < ' mir , ' tcx , Evaluator < ' tcx > > ,
45
- ) -> Pointer < Tag > {
50
+ ) -> InterpResult < ' tcx , Pointer < Tag > > {
46
51
let mut name_osstring = name. to_os_string ( ) ;
47
52
name_osstring. push ( "=" ) ;
48
53
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 ( ) ) ? )
50
55
}
51
56
52
57
impl < ' mir , ' tcx > EvalContextExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
53
58
pub 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 ( ) ;
56
61
57
62
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) {
61
65
Some ( var_ptr) => {
66
+ // The offset is used to strip the "{name}=" part of the string.
62
67
Scalar :: from ( var_ptr. offset ( Size :: from_bytes ( u64:: try_from ( name. len ( ) ) . unwrap ( ) . checked_add ( 1 ) . unwrap ( ) ) , this) ?)
63
68
}
64
69
None => Scalar :: ptr_null ( & * this. tcx ) ,
65
70
} )
66
71
}
67
72
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
+
68
127
fn setenv (
69
128
& mut self ,
70
129
name_op : OpTy < ' tcx , Tag > ,
@@ -74,34 +133,43 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
74
133
75
134
let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
76
135
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) ?;
78
137
let mut new = None ;
79
138
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) ?;
81
140
if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
82
141
new = Some ( ( name. to_owned ( ) , value. to_owned ( ) ) ) ;
83
142
}
84
143
}
85
144
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) ? ;
87
146
if let Some ( var) = this. machine . env_vars . map . insert ( name. to_owned ( ) , var_ptr) {
88
147
this. memory
89
148
. deallocate ( var, None , MiriMemoryKind :: Machine . into ( ) ) ?;
90
149
}
91
150
this. update_environ ( ) ?;
92
- Ok ( 0 )
151
+ Ok ( 0 ) // return zero on success
93
152
} else {
94
153
Ok ( -1 )
95
154
}
96
155
}
97
156
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
+
98
166
fn unsetenv ( & mut self , name_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
99
167
let this = self . eval_context_mut ( ) ;
100
168
101
169
let name_ptr = this. read_scalar ( name_op) ?. not_undef ( ) ?;
102
170
let mut success = None ;
103
171
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 ( ) ;
105
173
if !name. is_empty ( ) && !name. to_string_lossy ( ) . contains ( '=' ) {
106
174
success = Some ( this. machine . env_vars . map . remove ( & name) ) ;
107
175
}
0 commit comments