@@ -25,49 +25,64 @@ pub(crate) enum FlockOp {
2525pub trait FileDescription : std:: fmt:: Debug + Any {
2626 fn name ( & self ) -> & ' static str ;
2727
28- /// Reads as much as possible into the given buffer, and returns the number of bytes read.
28+ /// Reads as much as possible into the given buffer `ptr`.
29+ /// `len` indicates how many bytes we should try to read.
30+ /// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error.
2931 fn read < ' tcx > (
3032 & self ,
3133 _self_ref : & FileDescriptionRef ,
3234 _communicate_allowed : bool ,
33- _bytes : & mut [ u8 ] ,
35+ _ptr : Pointer ,
36+ _len : usize ,
37+ _dest : & MPlaceTy < ' tcx > ,
3438 _ecx : & mut MiriInterpCx < ' tcx > ,
35- ) -> InterpResult < ' tcx , io :: Result < usize > > {
39+ ) -> InterpResult < ' tcx > {
3640 throw_unsup_format ! ( "cannot read from {}" , self . name( ) ) ;
3741 }
3842
39- /// Writes as much as possible from the given buffer, and returns the number of bytes written.
43+ /// Writes as much as possible from the given buffer `ptr`.
44+ /// `len` indicates how many bytes we should try to write.
45+ /// `dest` is where the return value should be stored: number of bytes written, or `-1` in case of error.
4046 fn write < ' tcx > (
4147 & self ,
4248 _self_ref : & FileDescriptionRef ,
4349 _communicate_allowed : bool ,
44- _bytes : & [ u8 ] ,
50+ _ptr : Pointer ,
51+ _len : usize ,
52+ _dest : & MPlaceTy < ' tcx > ,
4553 _ecx : & mut MiriInterpCx < ' tcx > ,
46- ) -> InterpResult < ' tcx , io :: Result < usize > > {
54+ ) -> InterpResult < ' tcx > {
4755 throw_unsup_format ! ( "cannot write to {}" , self . name( ) ) ;
4856 }
4957
50- /// Reads as much as possible into the given buffer from a given offset,
51- /// and returns the number of bytes read.
58+ /// Reads as much as possible into the given buffer `ptr` from a given offset.
59+ /// `len` indicates how many bytes we should try to read.
60+ /// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error.
5261 fn pread < ' tcx > (
5362 & self ,
5463 _communicate_allowed : bool ,
55- _bytes : & mut [ u8 ] ,
5664 _offset : u64 ,
65+ _ptr : Pointer ,
66+ _len : usize ,
67+ _dest : & MPlaceTy < ' tcx > ,
5768 _ecx : & mut MiriInterpCx < ' tcx > ,
58- ) -> InterpResult < ' tcx , io :: Result < usize > > {
69+ ) -> InterpResult < ' tcx > {
5970 throw_unsup_format ! ( "cannot pread from {}" , self . name( ) ) ;
6071 }
6172
62- /// Writes as much as possible from the given buffer starting at a given offset,
63- /// and returns the number of bytes written.
73+ /// Writes as much as possible from the given buffer `ptr` starting at a given offset.
74+ /// `ptr` is the pointer to the user supplied read buffer.
75+ /// `len` indicates how many bytes we should try to write.
76+ /// `dest` is where the return value should be stored: number of bytes written, or `-1` in case of error.
6477 fn pwrite < ' tcx > (
6578 & self ,
6679 _communicate_allowed : bool ,
67- _bytes : & [ u8 ] ,
80+ _ptr : Pointer ,
81+ _len : usize ,
6882 _offset : u64 ,
83+ _dest : & MPlaceTy < ' tcx > ,
6984 _ecx : & mut MiriInterpCx < ' tcx > ,
70- ) -> InterpResult < ' tcx , io :: Result < usize > > {
85+ ) -> InterpResult < ' tcx > {
7186 throw_unsup_format ! ( "cannot pwrite to {}" , self . name( ) ) ;
7287 }
7388
@@ -125,14 +140,18 @@ impl FileDescription for io::Stdin {
125140 & self ,
126141 _self_ref : & FileDescriptionRef ,
127142 communicate_allowed : bool ,
128- bytes : & mut [ u8 ] ,
129- _ecx : & mut MiriInterpCx < ' tcx > ,
130- ) -> InterpResult < ' tcx , io:: Result < usize > > {
143+ ptr : Pointer ,
144+ len : usize ,
145+ dest : & MPlaceTy < ' tcx > ,
146+ ecx : & mut MiriInterpCx < ' tcx > ,
147+ ) -> InterpResult < ' tcx > {
148+ let mut bytes = vec ! [ 0 ; len] ;
131149 if !communicate_allowed {
132150 // We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
133151 helpers:: isolation_abort_error ( "`read` from stdin" ) ?;
134152 }
135- Ok ( Read :: read ( & mut { self } , bytes) )
153+ let result = Read :: read ( & mut { self } , & mut bytes) ;
154+ ecx. return_read_bytes_and_count ( ptr, & bytes, result, dest)
136155 }
137156
138157 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -149,9 +168,12 @@ impl FileDescription for io::Stdout {
149168 & self ,
150169 _self_ref : & FileDescriptionRef ,
151170 _communicate_allowed : bool ,
152- bytes : & [ u8 ] ,
153- _ecx : & mut MiriInterpCx < ' tcx > ,
154- ) -> InterpResult < ' tcx , io:: Result < usize > > {
171+ ptr : Pointer ,
172+ len : usize ,
173+ dest : & MPlaceTy < ' tcx > ,
174+ ecx : & mut MiriInterpCx < ' tcx > ,
175+ ) -> InterpResult < ' tcx > {
176+ let bytes = ecx. read_bytes_ptr_strip_provenance ( ptr, Size :: from_bytes ( len) ) ?;
155177 // We allow writing to stderr even with isolation enabled.
156178 let result = Write :: write ( & mut { self } , bytes) ;
157179 // Stdout is buffered, flush to make sure it appears on the
@@ -160,8 +182,7 @@ impl FileDescription for io::Stdout {
160182 // the host -- there is no good in adding extra buffering
161183 // here.
162184 io:: stdout ( ) . flush ( ) . unwrap ( ) ;
163-
164- Ok ( result)
185+ ecx. return_written_byte_count_or_error ( result, dest)
165186 }
166187
167188 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -178,12 +199,16 @@ impl FileDescription for io::Stderr {
178199 & self ,
179200 _self_ref : & FileDescriptionRef ,
180201 _communicate_allowed : bool ,
181- bytes : & [ u8 ] ,
182- _ecx : & mut MiriInterpCx < ' tcx > ,
183- ) -> InterpResult < ' tcx , io:: Result < usize > > {
202+ ptr : Pointer ,
203+ len : usize ,
204+ dest : & MPlaceTy < ' tcx > ,
205+ ecx : & mut MiriInterpCx < ' tcx > ,
206+ ) -> InterpResult < ' tcx > {
207+ let bytes = ecx. read_bytes_ptr_strip_provenance ( ptr, Size :: from_bytes ( len) ) ?;
184208 // We allow writing to stderr even with isolation enabled.
185209 // No need to flush, stderr is not buffered.
186- Ok ( Write :: write ( & mut { self } , bytes) )
210+ let result = Write :: write ( & mut { self } , bytes) ;
211+ ecx. return_written_byte_count_or_error ( result, dest)
187212 }
188213
189214 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -204,11 +229,14 @@ impl FileDescription for NullOutput {
204229 & self ,
205230 _self_ref : & FileDescriptionRef ,
206231 _communicate_allowed : bool ,
207- bytes : & [ u8 ] ,
208- _ecx : & mut MiriInterpCx < ' tcx > ,
209- ) -> InterpResult < ' tcx , io:: Result < usize > > {
232+ _ptr : Pointer ,
233+ len : usize ,
234+ dest : & MPlaceTy < ' tcx > ,
235+ ecx : & mut MiriInterpCx < ' tcx > ,
236+ ) -> InterpResult < ' tcx > {
210237 // We just don't write anything, but report to the user that we did.
211- Ok ( Ok ( bytes. len ( ) ) )
238+ let result = Ok ( len) ;
239+ ecx. return_written_byte_count_or_error ( result, dest)
212240 }
213241}
214242
@@ -535,7 +563,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
535563 buf : Pointer ,
536564 count : u64 ,
537565 offset : Option < i128 > ,
538- ) -> InterpResult < ' tcx , Scalar > {
566+ dest : & MPlaceTy < ' tcx > ,
567+ ) -> InterpResult < ' tcx > {
539568 let this = self . eval_context_mut ( ) ;
540569
541570 // Isolation check is done via `FileDescription` trait.
@@ -550,48 +579,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
550579 let count = count
551580 . min ( u64:: try_from ( this. target_isize_max ( ) ) . unwrap ( ) )
552581 . min ( u64:: try_from ( isize:: MAX ) . unwrap ( ) ) ;
582+ let count = usize:: try_from ( count) . unwrap ( ) ; // now it fits in a `usize`
553583 let communicate = this. machine . communicate ( ) ;
554584
555585 // We temporarily dup the FD to be able to retain mutable access to `this`.
556586 let Some ( fd) = this. machine . fds . get ( fd_num) else {
557587 trace ! ( "read: FD not found" ) ;
558- return Ok ( Scalar :: from_target_isize ( this. fd_not_found ( ) ?, this) ) ;
588+ let res: i32 = this. fd_not_found ( ) ?;
589+ this. write_int ( res, dest) ?;
590+ return Ok ( ( ) ) ;
559591 } ;
560592
561593 trace ! ( "read: FD mapped to {fd:?}" ) ;
562594 // We want to read at most `count` bytes. We are sure that `count` is not negative
563595 // because it was a target's `usize`. Also we are sure that its smaller than
564596 // `usize::MAX` because it is bounded by the host's `isize`.
565- let mut bytes = vec ! [ 0 ; usize :: try_from ( count ) . unwrap ( ) ] ;
566- let result = match offset {
567- None => fd. read ( & fd, communicate, & mut bytes , this) ,
597+
598+ match offset {
599+ None => fd. read ( & fd, communicate, buf , count , dest , this) ? ,
568600 Some ( offset) => {
569601 let Ok ( offset) = u64:: try_from ( offset) else {
570602 let einval = this. eval_libc ( "EINVAL" ) ;
571603 this. set_last_error ( einval) ?;
572- return Ok ( Scalar :: from_target_isize ( -1 , this) ) ;
604+ this. write_int ( -1 , dest) ?;
605+ return Ok ( ( ) ) ;
573606 } ;
574- fd. pread ( communicate, & mut bytes , offset , this)
607+ fd. pread ( communicate, offset , buf , count , dest , this) ?
575608 }
576609 } ;
577-
578- // `File::read` never returns a value larger than `count`, so this cannot fail.
579- match result?. map ( |c| i64:: try_from ( c) . unwrap ( ) ) {
580- Ok ( read_bytes) => {
581- // If reading to `bytes` did not fail, we write those bytes to the buffer.
582- // Crucially, if fewer than `bytes.len()` bytes were read, only write
583- // that much into the output buffer!
584- this. write_bytes_ptr (
585- buf,
586- bytes[ ..usize:: try_from ( read_bytes) . unwrap ( ) ] . iter ( ) . copied ( ) ,
587- ) ?;
588- Ok ( Scalar :: from_target_isize ( read_bytes, this) )
589- }
590- Err ( e) => {
591- this. set_last_error_from_io_error ( e) ?;
592- Ok ( Scalar :: from_target_isize ( -1 , this) )
593- }
594- }
610+ Ok ( ( ) )
595611 }
596612
597613 fn write (
@@ -600,7 +616,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
600616 buf : Pointer ,
601617 count : u64 ,
602618 offset : Option < i128 > ,
603- ) -> InterpResult < ' tcx , Scalar > {
619+ dest : & MPlaceTy < ' tcx > ,
620+ ) -> InterpResult < ' tcx > {
604621 let this = self . eval_context_mut ( ) ;
605622
606623 // Isolation check is done via `FileDescription` trait.
@@ -613,27 +630,72 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
613630 let count = count
614631 . min ( u64:: try_from ( this. target_isize_max ( ) ) . unwrap ( ) )
615632 . min ( u64:: try_from ( isize:: MAX ) . unwrap ( ) ) ;
633+ let count = usize:: try_from ( count) . unwrap ( ) ; // now it fits in a `usize`
616634 let communicate = this. machine . communicate ( ) ;
617635
618- let bytes = this. read_bytes_ptr_strip_provenance ( buf, Size :: from_bytes ( count) ) ?. to_owned ( ) ;
619636 // We temporarily dup the FD to be able to retain mutable access to `this`.
620637 let Some ( fd) = this. machine . fds . get ( fd_num) else {
621- return Ok ( Scalar :: from_target_isize ( this. fd_not_found ( ) ?, this) ) ;
638+ let res: i32 = this. fd_not_found ( ) ?;
639+ this. write_int ( res, dest) ?;
640+ return Ok ( ( ) ) ;
622641 } ;
623642
624- let result = match offset {
625- None => fd. write ( & fd, communicate, & bytes , this) ,
643+ match offset {
644+ None => fd. write ( & fd, communicate, buf , count , dest , this) ? ,
626645 Some ( offset) => {
627646 let Ok ( offset) = u64:: try_from ( offset) else {
628647 let einval = this. eval_libc ( "EINVAL" ) ;
629648 this. set_last_error ( einval) ?;
630- return Ok ( Scalar :: from_target_isize ( -1 , this) ) ;
649+ this. write_int ( -1 , dest) ?;
650+ return Ok ( ( ) ) ;
631651 } ;
632- fd. pwrite ( communicate, & bytes , offset, this)
652+ fd. pwrite ( communicate, buf , count , offset, dest , this) ?
633653 }
634654 } ;
655+ Ok ( ( ) )
656+ }
635657
636- let result = result?. map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
637- Ok ( Scalar :: from_target_isize ( this. try_unwrap_io_result ( result) ?, this) )
658+ /// Helper to implement `FileDescription::read`:
659+ /// `result` should be the return value of some underlying `read` call that used `bytes` as its output buffer.
660+ /// The length of `bytes` must not exceed either the host's or the target's `isize`.
661+ /// If `Result` indicates success, `bytes` is written to `buf` and the size is written to `dest`.
662+ /// Otherwise, `-1` is written to `dest` and the last libc error is set appropriately.
663+ fn return_read_bytes_and_count (
664+ & mut self ,
665+ buf : Pointer ,
666+ bytes : & [ u8 ] ,
667+ result : io:: Result < usize > ,
668+ dest : & MPlaceTy < ' tcx > ,
669+ ) -> InterpResult < ' tcx > {
670+ let this = self . eval_context_mut ( ) ;
671+ match result {
672+ Ok ( read_bytes) => {
673+ // If reading to `bytes` did not fail, we write those bytes to the buffer.
674+ // Crucially, if fewer than `bytes.len()` bytes were read, only write
675+ // that much into the output buffer!
676+ this. write_bytes_ptr ( buf, bytes[ ..read_bytes] . iter ( ) . copied ( ) ) ?;
677+ // The actual read size is always less than what got originally requested so this cannot fail.
678+ this. write_int ( u64:: try_from ( read_bytes) . unwrap ( ) , dest) ?;
679+ return Ok ( ( ) ) ;
680+ }
681+ Err ( e) => {
682+ this. set_last_error_from_io_error ( e) ?;
683+ this. write_int ( -1 , dest) ?;
684+ return Ok ( ( ) ) ;
685+ }
686+ }
687+ }
688+
689+ /// This function writes the number of written bytes (given in `result`) to `dest`, or sets the
690+ /// last libc error and writes -1 to dest.
691+ fn return_written_byte_count_or_error (
692+ & mut self ,
693+ result : io:: Result < usize > ,
694+ dest : & MPlaceTy < ' tcx > ,
695+ ) -> InterpResult < ' tcx > {
696+ let this = self . eval_context_mut ( ) ;
697+ let result = this. try_unwrap_io_result ( result. map ( |c| i64:: try_from ( c) . unwrap ( ) ) ) ?;
698+ this. write_int ( result, dest) ?;
699+ Ok ( ( ) )
638700 }
639701}
0 commit comments