@@ -26,13 +26,16 @@ pub trait FileDescription: std::fmt::Debug + Any {
2626 fn name ( & self ) -> & ' static str ;
2727
2828 /// Reads as much as possible into the given buffer, and returns the number of bytes read.
29+ /// `ptr` is the pointer to user supplied read buffer.
2930 fn read < ' tcx > (
3031 & self ,
3132 _self_ref : & FileDescriptionRef ,
3233 _communicate_allowed : bool ,
33- _bytes : & mut [ u8 ] ,
34+ _ptr : Pointer ,
35+ _len : u64 ,
36+ _dest : & MPlaceTy < ' tcx > ,
3437 _ecx : & mut MiriInterpCx < ' tcx > ,
35- ) -> InterpResult < ' tcx , io :: Result < usize > > {
38+ ) -> InterpResult < ' tcx > {
3639 throw_unsup_format ! ( "cannot read from {}" , self . name( ) ) ;
3740 }
3841
@@ -42,8 +45,9 @@ pub trait FileDescription: std::fmt::Debug + Any {
4245 _self_ref : & FileDescriptionRef ,
4346 _communicate_allowed : bool ,
4447 _bytes : & [ u8 ] ,
48+ _dest : & MPlaceTy < ' tcx > ,
4549 _ecx : & mut MiriInterpCx < ' tcx > ,
46- ) -> InterpResult < ' tcx , io :: Result < usize > > {
50+ ) -> InterpResult < ' tcx > {
4751 throw_unsup_format ! ( "cannot write to {}" , self . name( ) ) ;
4852 }
4953
@@ -52,10 +56,12 @@ pub trait FileDescription: std::fmt::Debug + Any {
5256 fn pread < ' tcx > (
5357 & self ,
5458 _communicate_allowed : bool ,
55- _bytes : & mut [ u8 ] ,
5659 _offset : u64 ,
60+ _ptr : Pointer ,
61+ _len : u64 ,
62+ _dest : & MPlaceTy < ' tcx > ,
5763 _ecx : & mut MiriInterpCx < ' tcx > ,
58- ) -> InterpResult < ' tcx , io :: Result < usize > > {
64+ ) -> InterpResult < ' tcx > {
5965 throw_unsup_format ! ( "cannot pread from {}" , self . name( ) ) ;
6066 }
6167
@@ -66,8 +72,9 @@ pub trait FileDescription: std::fmt::Debug + Any {
6672 _communicate_allowed : bool ,
6773 _bytes : & [ u8 ] ,
6874 _offset : u64 ,
75+ _dest : & MPlaceTy < ' tcx > ,
6976 _ecx : & mut MiriInterpCx < ' tcx > ,
70- ) -> InterpResult < ' tcx , io :: Result < usize > > {
77+ ) -> InterpResult < ' tcx > {
7178 throw_unsup_format ! ( "cannot pwrite to {}" , self . name( ) ) ;
7279 }
7380
@@ -125,14 +132,18 @@ impl FileDescription for io::Stdin {
125132 & self ,
126133 _self_ref : & FileDescriptionRef ,
127134 communicate_allowed : bool ,
128- bytes : & mut [ u8 ] ,
129- _ecx : & mut MiriInterpCx < ' tcx > ,
130- ) -> InterpResult < ' tcx , io:: Result < usize > > {
135+ ptr : Pointer ,
136+ len : u64 ,
137+ dest : & MPlaceTy < ' tcx > ,
138+ ecx : & mut MiriInterpCx < ' tcx > ,
139+ ) -> InterpResult < ' tcx > {
140+ let mut bytes = vec ! [ 0 ; usize :: try_from( len) . unwrap( ) ] ;
131141 if !communicate_allowed {
132142 // We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
133143 helpers:: isolation_abort_error ( "`read` from stdin" ) ?;
134144 }
135- Ok ( Read :: read ( & mut { self } , bytes) )
145+ let result = Read :: read ( & mut { self } , & mut bytes) ;
146+ ecx. return_read_bytes_and_count ( ptr, bytes, result, dest)
136147 }
137148
138149 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -150,8 +161,9 @@ impl FileDescription for io::Stdout {
150161 _self_ref : & FileDescriptionRef ,
151162 _communicate_allowed : bool ,
152163 bytes : & [ u8 ] ,
153- _ecx : & mut MiriInterpCx < ' tcx > ,
154- ) -> InterpResult < ' tcx , io:: Result < usize > > {
164+ dest : & MPlaceTy < ' tcx > ,
165+ ecx : & mut MiriInterpCx < ' tcx > ,
166+ ) -> InterpResult < ' tcx > {
155167 // We allow writing to stderr even with isolation enabled.
156168 let result = Write :: write ( & mut { self } , bytes) ;
157169 // Stdout is buffered, flush to make sure it appears on the
@@ -160,8 +172,7 @@ impl FileDescription for io::Stdout {
160172 // the host -- there is no good in adding extra buffering
161173 // here.
162174 io:: stdout ( ) . flush ( ) . unwrap ( ) ;
163-
164- Ok ( result)
175+ ecx. return_written_byte_count_or_error ( result, dest)
165176 }
166177
167178 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -179,11 +190,13 @@ impl FileDescription for io::Stderr {
179190 _self_ref : & FileDescriptionRef ,
180191 _communicate_allowed : bool ,
181192 bytes : & [ u8 ] ,
182- _ecx : & mut MiriInterpCx < ' tcx > ,
183- ) -> InterpResult < ' tcx , io:: Result < usize > > {
193+ dest : & MPlaceTy < ' tcx > ,
194+ ecx : & mut MiriInterpCx < ' tcx > ,
195+ ) -> InterpResult < ' tcx > {
184196 // We allow writing to stderr even with isolation enabled.
185197 // No need to flush, stderr is not buffered.
186- Ok ( Write :: write ( & mut { self } , bytes) )
198+ let result = Write :: write ( & mut { self } , bytes) ;
199+ ecx. return_written_byte_count_or_error ( result, dest)
187200 }
188201
189202 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -205,10 +218,12 @@ impl FileDescription for NullOutput {
205218 _self_ref : & FileDescriptionRef ,
206219 _communicate_allowed : bool ,
207220 bytes : & [ u8 ] ,
208- _ecx : & mut MiriInterpCx < ' tcx > ,
209- ) -> InterpResult < ' tcx , io:: Result < usize > > {
221+ dest : & MPlaceTy < ' tcx > ,
222+ ecx : & mut MiriInterpCx < ' tcx > ,
223+ ) -> InterpResult < ' tcx > {
210224 // We just don't write anything, but report to the user that we did.
211- Ok ( Ok ( bytes. len ( ) ) )
225+ let result = Ok ( bytes. len ( ) ) ;
226+ ecx. return_written_byte_count_or_error ( result, dest)
212227 }
213228}
214229
@@ -535,7 +550,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
535550 buf : Pointer ,
536551 count : u64 ,
537552 offset : Option < i128 > ,
538- ) -> InterpResult < ' tcx , Scalar > {
553+ dest : & MPlaceTy < ' tcx > ,
554+ ) -> InterpResult < ' tcx > {
539555 let this = self . eval_context_mut ( ) ;
540556
541557 // Isolation check is done via `FileDescription` trait.
@@ -555,43 +571,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
555571 // We temporarily dup the FD to be able to retain mutable access to `this`.
556572 let Some ( fd) = this. machine . fds . get ( fd_num) else {
557573 trace ! ( "read: FD not found" ) ;
558- return Ok ( Scalar :: from_target_isize ( this. fd_not_found ( ) ?, this) ) ;
574+ let res: i32 = this. fd_not_found ( ) ?;
575+ this. write_int ( res, dest) ?;
576+ return Ok ( ( ) ) ;
559577 } ;
560578
561579 trace ! ( "read: FD mapped to {fd:?}" ) ;
562580 // We want to read at most `count` bytes. We are sure that `count` is not negative
563581 // because it was a target's `usize`. Also we are sure that its smaller than
564582 // `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) ,
583+
584+ match offset {
585+ None => fd. read ( & fd, communicate, buf , count , dest , this) ? ,
568586 Some ( offset) => {
569587 let Ok ( offset) = u64:: try_from ( offset) else {
570588 let einval = this. eval_libc ( "EINVAL" ) ;
571589 this. set_last_error ( einval) ?;
572- return Ok ( Scalar :: from_target_isize ( -1 , this) ) ;
590+ this. write_int ( -1 , dest) ?;
591+ return Ok ( ( ) ) ;
573592 } ;
574- fd. pread ( communicate, & mut bytes , offset , this)
593+ fd. pread ( communicate, offset , buf , count , dest , this) ?
575594 }
576595 } ;
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- }
596+ Ok ( ( ) )
595597 }
596598
597599 fn write (
@@ -600,7 +602,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
600602 buf : Pointer ,
601603 count : u64 ,
602604 offset : Option < i128 > ,
603- ) -> InterpResult < ' tcx , Scalar > {
605+ dest : & MPlaceTy < ' tcx > ,
606+ ) -> InterpResult < ' tcx > {
604607 let this = self . eval_context_mut ( ) ;
605608
606609 // Isolation check is done via `FileDescription` trait.
@@ -618,22 +621,64 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
618621 let bytes = this. read_bytes_ptr_strip_provenance ( buf, Size :: from_bytes ( count) ) ?. to_owned ( ) ;
619622 // We temporarily dup the FD to be able to retain mutable access to `this`.
620623 let Some ( fd) = this. machine . fds . get ( fd_num) else {
621- return Ok ( Scalar :: from_target_isize ( this. fd_not_found ( ) ?, this) ) ;
624+ let res: i32 = this. fd_not_found ( ) ?;
625+ this. write_int ( res, dest) ?;
626+ return Ok ( ( ) ) ;
622627 } ;
623628
624- let result = match offset {
625- None => fd. write ( & fd, communicate, & bytes, this) ,
629+ match offset {
630+ None => fd. write ( & fd, communicate, & bytes, dest , this) ? ,
626631 Some ( offset) => {
627632 let Ok ( offset) = u64:: try_from ( offset) else {
628633 let einval = this. eval_libc ( "EINVAL" ) ;
629634 this. set_last_error ( einval) ?;
630- return Ok ( Scalar :: from_target_isize ( -1 , this) ) ;
635+ this. write_int ( -1 , dest) ?;
636+ return Ok ( ( ) ) ;
631637 } ;
632- fd. pwrite ( communicate, & bytes, offset, this)
638+ fd. pwrite ( communicate, & bytes, offset, dest , this) ?
633639 }
634640 } ;
641+ Ok ( ( ) )
642+ }
635643
636- let result = result?. map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
637- Ok ( Scalar :: from_target_isize ( this. try_unwrap_io_result ( result) ?, this) )
644+ /// This function either writes to the user supplied buffer and to dest place, or sets the
645+ /// last libc error and writes -1 to dest.
646+ fn return_read_bytes_and_count (
647+ & mut self ,
648+ buf : Pointer ,
649+ bytes : Vec < u8 > ,
650+ result : io:: Result < usize > ,
651+ dest : & MPlaceTy < ' tcx > ,
652+ ) -> InterpResult < ' tcx > {
653+ let this = self . eval_context_mut ( ) ;
654+ match result {
655+ Ok ( read_bytes) => {
656+ // If reading to `bytes` did not fail, we write those bytes to the buffer.
657+ // Crucially, if fewer than `bytes.len()` bytes were read, only write
658+ // that much into the output buffer!
659+ this. write_bytes_ptr ( buf, bytes[ ..read_bytes] . iter ( ) . copied ( ) ) ?;
660+ // The actual read size is always lesser than `count` so this cannot fail.
661+ this. write_int ( u64:: try_from ( read_bytes) . unwrap ( ) , dest) ?;
662+ return Ok ( ( ) ) ;
663+ }
664+ Err ( e) => {
665+ this. set_last_error_from_io_error ( e) ?;
666+ this. write_int ( -1 , dest) ?;
667+ return Ok ( ( ) ) ;
668+ }
669+ }
670+ }
671+
672+ /// This function writes the number of written bytes to dest place, or sets the
673+ /// last libc error and writes -1 to dest.
674+ fn return_written_byte_count_or_error (
675+ & mut self ,
676+ result : io:: Result < usize > ,
677+ dest : & MPlaceTy < ' tcx > ,
678+ ) -> InterpResult < ' tcx > {
679+ let this = self . eval_context_mut ( ) ;
680+ let result = this. try_unwrap_io_result ( result. map ( |c| i64:: try_from ( c) . unwrap ( ) ) ) ?;
681+ this. write_int ( result, dest) ?;
682+ Ok ( ( ) )
638683 }
639684}
0 commit comments