1
1
use std:: collections:: HashMap ;
2
- use std:: fs:: { File , OpenOptions , remove_file} ;
2
+ use std:: convert:: TryFrom ;
3
+ use std:: fs:: { remove_file, File , OpenOptions } ;
3
4
use std:: io:: { Read , Write } ;
4
5
5
6
use rustc:: ty:: layout:: Size ;
@@ -125,8 +126,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
125
126
// `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
126
127
// always sets this flag when opening a file. However we still need to check that the
127
128
// file itself is open.
128
- let fd_cloexec = this. eval_libc_i32 ( "FD_CLOEXEC" ) ?;
129
- this. get_handle_and ( fd, |_| Ok ( fd_cloexec) )
129
+ if this. machine . file_handler . handles . contains_key ( & fd) {
130
+ Ok ( this. eval_libc_i32 ( "FD_CLOEXEC" ) ?)
131
+ } else {
132
+ this. handle_not_found ( )
133
+ }
130
134
} else {
131
135
throw_unsup_format ! ( "The {:#x} command is not supported for `fcntl`)" , cmd) ;
132
136
}
@@ -139,9 +143,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
139
143
140
144
let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
141
145
142
- this. remove_handle_and ( fd, |handle, this| {
143
- this. try_unwrap_io_result ( handle. file . sync_all ( ) . map ( |_| 0i32 ) )
144
- } )
146
+ if let Some ( handle) = this. machine . file_handler . handles . remove ( & fd) {
147
+ // `File::sync_all` does the checks that are done when closing a file. We do this to
148
+ // to handle possible errors correctly.
149
+ let result = this. try_unwrap_io_result ( handle. file . sync_all ( ) . map ( |_| 0i32 ) ) ;
150
+ // Now we actually close the file.
151
+ drop ( handle) ;
152
+ // And return the result.
153
+ result
154
+ } else {
155
+ this. handle_not_found ( )
156
+ }
145
157
}
146
158
147
159
fn read (
@@ -154,27 +166,50 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
154
166
155
167
this. check_no_isolation ( "read" ) ?;
156
168
157
- let count = this. read_scalar ( count_op) ?. to_machine_usize ( & * this. tcx ) ?;
169
+ let ptr_size = this. pointer_size ( ) . bits ( ) ;
170
+
171
+ // We cap the number of read bytes to the largest value that we are able to fit in both the
172
+ // host's and target's `isize`.
173
+ let count = this
174
+ . read_scalar ( count_op) ?
175
+ . to_machine_usize ( & * this. tcx ) ?
176
+ . min ( ( 1 << ( ptr_size - 1 ) ) - 1 ) // max value of target `isize`
177
+ . min ( isize:: max_value ( ) as u64 ) ;
158
178
// Reading zero bytes should not change `buf`.
159
179
if count == 0 {
160
180
return Ok ( 0 ) ;
161
181
}
162
182
let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
163
- let buf_scalar = this. read_scalar ( buf_op) ?. not_undef ( ) ?;
164
-
165
- // Remove the file handle to avoid borrowing issues.
166
- this. remove_handle_and ( fd, |mut handle, this| {
167
- // Don't use `?` to avoid returning before reinserting the handle.
168
- let bytes = this. force_ptr ( buf_scalar) . and_then ( |buf| {
169
- // FIXME: Don't use raw methods
170
- this. memory
171
- . get_raw_mut ( buf. alloc_id ) ?
172
- . get_bytes_mut ( & * this. tcx , buf, Size :: from_bytes ( count) )
173
- . map ( |buffer| handle. file . read ( buffer) )
174
- } ) ;
175
- this. machine . file_handler . handles . insert ( fd, handle) . unwrap_none ( ) ;
176
- this. try_unwrap_io_result ( bytes?. map ( |bytes| bytes as i64 ) )
177
- } )
183
+ let buf = this. read_scalar ( buf_op) ?. not_undef ( ) ?;
184
+
185
+ if let Some ( handle) = this. machine . file_handler . handles . get_mut ( & fd) {
186
+ // This can never fail because `count` was capped to be smaller than
187
+ // `isize::max_value()`.
188
+ let count = isize:: try_from ( count) . unwrap ( ) ;
189
+ // We want to read at most `count` bytes. We are sure that `count` is not negative
190
+ // because it was a target's `usize`. Also we are sure that its smaller than
191
+ // `usize::max_value()` because it is a host's `isize`.
192
+ let mut bytes = vec ! [ 0 ; count as usize ] ;
193
+ let result = handle
194
+ . file
195
+ . read ( & mut bytes)
196
+ // `File::read` never returns a value larger than `count`, so this cannot fail.
197
+ . map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
198
+
199
+ match result {
200
+ Ok ( read_bytes) => {
201
+ // If reading to `bytes` did not fail, we write those bytes to the buffer.
202
+ this. memory . write_bytes ( buf, bytes) ?;
203
+ Ok ( read_bytes)
204
+ }
205
+ Err ( e) => {
206
+ this. set_last_error_from_io_error ( e) ?;
207
+ Ok ( -1 )
208
+ }
209
+ }
210
+ } else {
211
+ this. handle_not_found ( )
212
+ }
178
213
}
179
214
180
215
fn write (
@@ -187,27 +222,32 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
187
222
188
223
this. check_no_isolation ( "write" ) ?;
189
224
190
- let count = this. read_scalar ( count_op) ?. to_machine_usize ( & * this. tcx ) ?;
225
+ let ptr_size = this. pointer_size ( ) . bits ( ) ;
226
+
227
+ // We cap the number of read bytes to the largest value that we are able to fit in both the
228
+ // host's and target's `isize`.
229
+ let count = this
230
+ . read_scalar ( count_op) ?
231
+ . to_machine_usize ( & * this. tcx ) ?
232
+ . min ( ( 1 << ( ptr_size - 1 ) ) - 1 ) // max value of target `isize`
233
+ . min ( isize:: max_value ( ) as u64 ) ;
191
234
// Writing zero bytes should not change `buf`.
192
235
if count == 0 {
193
236
return Ok ( 0 ) ;
194
237
}
195
238
let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
196
- let buf = this. force_ptr ( this. read_scalar ( buf_op) ?. not_undef ( ) ?) ?;
197
-
198
- this. remove_handle_and ( fd, |mut handle, this| {
199
- // FIXME: Don't use raw methods
200
- let bytes = this. memory . get_raw ( buf. alloc_id ) . and_then ( |alloc| {
201
- alloc
202
- . get_bytes ( & * this. tcx , buf, Size :: from_bytes ( count) )
203
- . map ( |bytes| handle. file . write ( bytes) . map ( |bytes| bytes as i64 ) )
204
- } ) ;
205
- this. machine . file_handler . handles . insert ( fd, handle) . unwrap_none ( ) ;
206
- this. try_unwrap_io_result ( bytes?)
207
- } )
239
+ let buf = this. read_scalar ( buf_op) ?. not_undef ( ) ?;
240
+
241
+ if let Some ( handle) = this. machine . file_handler . handles . get_mut ( & fd) {
242
+ let bytes = this. memory . read_bytes ( buf, Size :: from_bytes ( count) ) ?;
243
+ let result = handle. file . write ( & bytes) . map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
244
+ this. try_unwrap_io_result ( result)
245
+ } else {
246
+ this. handle_not_found ( )
247
+ }
208
248
}
209
249
210
- fn unlink ( & mut self , path_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
250
+ fn unlink ( & mut self , path_op : OpTy < ' tcx , Tag > ) -> InterpResult < ' tcx , i32 > {
211
251
let this = self . eval_context_mut ( ) ;
212
252
213
253
this. check_no_isolation ( "unlink" ) ?;
@@ -219,49 +259,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
219
259
this. try_unwrap_io_result ( result)
220
260
}
221
261
222
- /// Helper function that gets a `FileHandle` immutable reference and allows to manipulate it
223
- /// using the `f` closure.
224
- ///
225
- /// If the `fd` file descriptor does not correspond to a file, this functions returns `Ok(-1)`
226
- /// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
227
- ///
228
- /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
229
- /// functions return different integer types (like `read`, that returns an `i64`).
230
- fn get_handle_and < F , T : From < i32 > > ( & mut self , fd : i32 , f : F ) -> InterpResult < ' tcx , T >
231
- where
232
- F : Fn ( & FileHandle ) -> InterpResult < ' tcx , T > ,
233
- {
262
+ /// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
263
+ /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
264
+ /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
265
+ /// types (like `read`, that returns an `i64`).
266
+ fn handle_not_found < T : From < i32 > > ( & mut self ) -> InterpResult < ' tcx , T > {
234
267
let this = self . eval_context_mut ( ) ;
235
- if let Some ( handle) = this. machine . file_handler . handles . get ( & fd) {
236
- f ( handle)
237
- } else {
238
- let ebadf = this. eval_libc ( "EBADF" ) ?;
239
- this. set_last_error ( ebadf) ?;
240
- Ok ( ( -1 ) . into ( ) )
241
- }
242
- }
243
-
244
- /// Helper function that removes a `FileHandle` and allows to manipulate it using the `f`
245
- /// closure. This function is quite useful when you need to modify a `FileHandle` but you need
246
- /// to modify `MiriEvalContext` at the same time, so you can modify the handle and reinsert it
247
- /// using `f`.
248
- ///
249
- /// If the `fd` file descriptor does not correspond to a file, this functions returns `Ok(-1)`
250
- /// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
251
- ///
252
- /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
253
- /// functions return different integer types (like `read`, that returns an `i64`).
254
- fn remove_handle_and < F , T : From < i32 > > ( & mut self , fd : i32 , mut f : F ) -> InterpResult < ' tcx , T >
255
- where
256
- F : FnMut ( FileHandle , & mut MiriEvalContext < ' mir , ' tcx > ) -> InterpResult < ' tcx , T > ,
257
- {
258
- let this = self . eval_context_mut ( ) ;
259
- if let Some ( handle) = this. machine . file_handler . handles . remove ( & fd) {
260
- f ( handle, this)
261
- } else {
262
- let ebadf = this. eval_libc ( "EBADF" ) ?;
263
- this. set_last_error ( ebadf) ?;
264
- Ok ( ( -1 ) . into ( ) )
265
- }
268
+ let ebadf = this. eval_libc ( "EBADF" ) ?;
269
+ this. set_last_error ( ebadf) ?;
270
+ Ok ( ( -1 ) . into ( ) )
266
271
}
267
272
}
0 commit comments