@@ -26,13 +26,16 @@ pub trait FileDescription: std::fmt::Debug + Any {
26
26
fn name ( & self ) -> & ' static str ;
27
27
28
28
/// 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.
29
30
fn read < ' tcx > (
30
31
& self ,
31
32
_self_ref : & FileDescriptionRef ,
32
33
_communicate_allowed : bool ,
33
- _bytes : & mut [ u8 ] ,
34
+ _ptr : Pointer ,
35
+ _len : u64 ,
36
+ _dest : & MPlaceTy < ' tcx > ,
34
37
_ecx : & mut MiriInterpCx < ' tcx > ,
35
- ) -> InterpResult < ' tcx , io :: Result < usize > > {
38
+ ) -> InterpResult < ' tcx > {
36
39
throw_unsup_format ! ( "cannot read from {}" , self . name( ) ) ;
37
40
}
38
41
@@ -42,8 +45,9 @@ pub trait FileDescription: std::fmt::Debug + Any {
42
45
_self_ref : & FileDescriptionRef ,
43
46
_communicate_allowed : bool ,
44
47
_bytes : & [ u8 ] ,
48
+ _dest : & MPlaceTy < ' tcx > ,
45
49
_ecx : & mut MiriInterpCx < ' tcx > ,
46
- ) -> InterpResult < ' tcx , io :: Result < usize > > {
50
+ ) -> InterpResult < ' tcx > {
47
51
throw_unsup_format ! ( "cannot write to {}" , self . name( ) ) ;
48
52
}
49
53
@@ -52,10 +56,12 @@ pub trait FileDescription: std::fmt::Debug + Any {
52
56
fn pread < ' tcx > (
53
57
& self ,
54
58
_communicate_allowed : bool ,
55
- _bytes : & mut [ u8 ] ,
56
59
_offset : u64 ,
60
+ _ptr : Pointer ,
61
+ _len : u64 ,
62
+ _dest : & MPlaceTy < ' tcx > ,
57
63
_ecx : & mut MiriInterpCx < ' tcx > ,
58
- ) -> InterpResult < ' tcx , io :: Result < usize > > {
64
+ ) -> InterpResult < ' tcx > {
59
65
throw_unsup_format ! ( "cannot pread from {}" , self . name( ) ) ;
60
66
}
61
67
@@ -66,8 +72,9 @@ pub trait FileDescription: std::fmt::Debug + Any {
66
72
_communicate_allowed : bool ,
67
73
_bytes : & [ u8 ] ,
68
74
_offset : u64 ,
75
+ _dest : & MPlaceTy < ' tcx > ,
69
76
_ecx : & mut MiriInterpCx < ' tcx > ,
70
- ) -> InterpResult < ' tcx , io :: Result < usize > > {
77
+ ) -> InterpResult < ' tcx > {
71
78
throw_unsup_format ! ( "cannot pwrite to {}" , self . name( ) ) ;
72
79
}
73
80
@@ -125,14 +132,18 @@ impl FileDescription for io::Stdin {
125
132
& self ,
126
133
_self_ref : & FileDescriptionRef ,
127
134
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( ) ] ;
131
141
if !communicate_allowed {
132
142
// We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
133
143
helpers:: isolation_abort_error ( "`read` from stdin" ) ?;
134
144
}
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)
136
147
}
137
148
138
149
fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -150,8 +161,9 @@ impl FileDescription for io::Stdout {
150
161
_self_ref : & FileDescriptionRef ,
151
162
_communicate_allowed : bool ,
152
163
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 > {
155
167
// We allow writing to stderr even with isolation enabled.
156
168
let result = Write :: write ( & mut { self } , bytes) ;
157
169
// Stdout is buffered, flush to make sure it appears on the
@@ -160,8 +172,7 @@ impl FileDescription for io::Stdout {
160
172
// the host -- there is no good in adding extra buffering
161
173
// here.
162
174
io:: stdout ( ) . flush ( ) . unwrap ( ) ;
163
-
164
- Ok ( result)
175
+ ecx. return_written_byte_count_or_error ( result, dest)
165
176
}
166
177
167
178
fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -179,11 +190,13 @@ impl FileDescription for io::Stderr {
179
190
_self_ref : & FileDescriptionRef ,
180
191
_communicate_allowed : bool ,
181
192
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 > {
184
196
// We allow writing to stderr even with isolation enabled.
185
197
// 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)
187
200
}
188
201
189
202
fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -205,10 +218,12 @@ impl FileDescription for NullOutput {
205
218
_self_ref : & FileDescriptionRef ,
206
219
_communicate_allowed : bool ,
207
220
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 > {
210
224
// 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)
212
227
}
213
228
}
214
229
@@ -535,7 +550,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
535
550
buf : Pointer ,
536
551
count : u64 ,
537
552
offset : Option < i128 > ,
538
- ) -> InterpResult < ' tcx , Scalar > {
553
+ dest : & MPlaceTy < ' tcx > ,
554
+ ) -> InterpResult < ' tcx > {
539
555
let this = self . eval_context_mut ( ) ;
540
556
541
557
// Isolation check is done via `FileDescription` trait.
@@ -555,43 +571,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
555
571
// We temporarily dup the FD to be able to retain mutable access to `this`.
556
572
let Some ( fd) = this. machine . fds . get ( fd_num) else {
557
573
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 ( ( ) ) ;
559
577
} ;
560
578
561
579
trace ! ( "read: FD mapped to {fd:?}" ) ;
562
580
// We want to read at most `count` bytes. We are sure that `count` is not negative
563
581
// because it was a target's `usize`. Also we are sure that its smaller than
564
582
// `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) ? ,
568
586
Some ( offset) => {
569
587
let Ok ( offset) = u64:: try_from ( offset) else {
570
588
let einval = this. eval_libc ( "EINVAL" ) ;
571
589
this. set_last_error ( einval) ?;
572
- return Ok ( Scalar :: from_target_isize ( -1 , this) ) ;
590
+ this. write_int ( -1 , dest) ?;
591
+ return Ok ( ( ) ) ;
573
592
} ;
574
- fd. pread ( communicate, & mut bytes , offset , this)
593
+ fd. pread ( communicate, offset , buf , count , dest , this) ?
575
594
}
576
595
} ;
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 ( ( ) )
595
597
}
596
598
597
599
fn write (
@@ -600,7 +602,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
600
602
buf : Pointer ,
601
603
count : u64 ,
602
604
offset : Option < i128 > ,
603
- ) -> InterpResult < ' tcx , Scalar > {
605
+ dest : & MPlaceTy < ' tcx > ,
606
+ ) -> InterpResult < ' tcx > {
604
607
let this = self . eval_context_mut ( ) ;
605
608
606
609
// Isolation check is done via `FileDescription` trait.
@@ -618,22 +621,64 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
618
621
let bytes = this. read_bytes_ptr_strip_provenance ( buf, Size :: from_bytes ( count) ) ?. to_owned ( ) ;
619
622
// We temporarily dup the FD to be able to retain mutable access to `this`.
620
623
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 ( ( ) ) ;
622
627
} ;
623
628
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) ? ,
626
631
Some ( offset) => {
627
632
let Ok ( offset) = u64:: try_from ( offset) else {
628
633
let einval = this. eval_libc ( "EINVAL" ) ;
629
634
this. set_last_error ( einval) ?;
630
- return Ok ( Scalar :: from_target_isize ( -1 , this) ) ;
635
+ this. write_int ( -1 , dest) ?;
636
+ return Ok ( ( ) ) ;
631
637
} ;
632
- fd. pwrite ( communicate, & bytes, offset, this)
638
+ fd. pwrite ( communicate, & bytes, offset, dest , this) ?
633
639
}
634
640
} ;
641
+ Ok ( ( ) )
642
+ }
635
643
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 ( ( ) )
638
683
}
639
684
}
0 commit comments