11use std:: collections:: HashMap ;
2- use std:: fs:: { File , OpenOptions , remove_file} ;
2+ use std:: convert:: TryFrom ;
3+ use std:: fs:: { remove_file, File , OpenOptions } ;
34use std:: io:: { Read , Write } ;
45
56use rustc:: ty:: layout:: Size ;
@@ -125,8 +126,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
125126 // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
126127 // always sets this flag when opening a file. However we still need to check that the
127128 // 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+ }
130134 } else {
131135 throw_unsup_format ! ( "The {:#x} command is not supported for `fcntl`)" , cmd) ;
132136 }
@@ -139,9 +143,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
139143
140144 let fd = this. read_scalar ( fd_op) ?. to_i32 ( ) ?;
141145
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+ }
145157 }
146158
147159 fn read (
@@ -154,27 +166,50 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
154166
155167 this. check_no_isolation ( "read" ) ?;
156168
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 ) ;
158178 // Reading zero bytes should not change `buf`.
159179 if count == 0 {
160180 return Ok ( 0 ) ;
161181 }
162182 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+ }
178213 }
179214
180215 fn write (
@@ -187,27 +222,32 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
187222
188223 this. check_no_isolation ( "write" ) ?;
189224
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 ) ;
191234 // Writing zero bytes should not change `buf`.
192235 if count == 0 {
193236 return Ok ( 0 ) ;
194237 }
195238 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+ }
208248 }
209249
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 > {
211251 let this = self . eval_context_mut ( ) ;
212252
213253 this. check_no_isolation ( "unlink" ) ?;
@@ -219,49 +259,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
219259 this. try_unwrap_io_result ( result)
220260 }
221261
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 > {
234267 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 ( ) )
266271 }
267272}
0 commit comments