@@ -25,49 +25,64 @@ pub(crate) enum FlockOp {
25
25
pub trait FileDescription : std:: fmt:: Debug + Any {
26
26
fn name ( & self ) -> & ' static str ;
27
27
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.
29
31
fn read < ' tcx > (
30
32
& self ,
31
33
_self_ref : & FileDescriptionRef ,
32
34
_communicate_allowed : bool ,
33
- _bytes : & mut [ u8 ] ,
35
+ _ptr : Pointer ,
36
+ _len : usize ,
37
+ _dest : & MPlaceTy < ' tcx > ,
34
38
_ecx : & mut MiriInterpCx < ' tcx > ,
35
- ) -> InterpResult < ' tcx , io :: Result < usize > > {
39
+ ) -> InterpResult < ' tcx > {
36
40
throw_unsup_format ! ( "cannot read from {}" , self . name( ) ) ;
37
41
}
38
42
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.
40
46
fn write < ' tcx > (
41
47
& self ,
42
48
_self_ref : & FileDescriptionRef ,
43
49
_communicate_allowed : bool ,
44
- _bytes : & [ u8 ] ,
50
+ _ptr : Pointer ,
51
+ _len : usize ,
52
+ _dest : & MPlaceTy < ' tcx > ,
45
53
_ecx : & mut MiriInterpCx < ' tcx > ,
46
- ) -> InterpResult < ' tcx , io :: Result < usize > > {
54
+ ) -> InterpResult < ' tcx > {
47
55
throw_unsup_format ! ( "cannot write to {}" , self . name( ) ) ;
48
56
}
49
57
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.
52
61
fn pread < ' tcx > (
53
62
& self ,
54
63
_communicate_allowed : bool ,
55
- _bytes : & mut [ u8 ] ,
56
64
_offset : u64 ,
65
+ _ptr : Pointer ,
66
+ _len : usize ,
67
+ _dest : & MPlaceTy < ' tcx > ,
57
68
_ecx : & mut MiriInterpCx < ' tcx > ,
58
- ) -> InterpResult < ' tcx , io :: Result < usize > > {
69
+ ) -> InterpResult < ' tcx > {
59
70
throw_unsup_format ! ( "cannot pread from {}" , self . name( ) ) ;
60
71
}
61
72
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.
64
77
fn pwrite < ' tcx > (
65
78
& self ,
66
79
_communicate_allowed : bool ,
67
- _bytes : & [ u8 ] ,
80
+ _ptr : Pointer ,
81
+ _len : usize ,
68
82
_offset : u64 ,
83
+ _dest : & MPlaceTy < ' tcx > ,
69
84
_ecx : & mut MiriInterpCx < ' tcx > ,
70
- ) -> InterpResult < ' tcx , io :: Result < usize > > {
85
+ ) -> InterpResult < ' tcx > {
71
86
throw_unsup_format ! ( "cannot pwrite to {}" , self . name( ) ) ;
72
87
}
73
88
@@ -125,14 +140,18 @@ impl FileDescription for io::Stdin {
125
140
& self ,
126
141
_self_ref : & FileDescriptionRef ,
127
142
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] ;
131
149
if !communicate_allowed {
132
150
// We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
133
151
helpers:: isolation_abort_error ( "`read` from stdin" ) ?;
134
152
}
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)
136
155
}
137
156
138
157
fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -149,9 +168,12 @@ impl FileDescription for io::Stdout {
149
168
& self ,
150
169
_self_ref : & FileDescriptionRef ,
151
170
_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) ) ?;
155
177
// We allow writing to stderr even with isolation enabled.
156
178
let result = Write :: write ( & mut { self } , bytes) ;
157
179
// Stdout is buffered, flush to make sure it appears on the
@@ -160,8 +182,7 @@ impl FileDescription for io::Stdout {
160
182
// the host -- there is no good in adding extra buffering
161
183
// here.
162
184
io:: stdout ( ) . flush ( ) . unwrap ( ) ;
163
-
164
- Ok ( result)
185
+ ecx. return_written_byte_count_or_error ( result, dest)
165
186
}
166
187
167
188
fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -178,12 +199,16 @@ impl FileDescription for io::Stderr {
178
199
& self ,
179
200
_self_ref : & FileDescriptionRef ,
180
201
_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) ) ?;
184
208
// We allow writing to stderr even with isolation enabled.
185
209
// 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)
187
212
}
188
213
189
214
fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -204,11 +229,14 @@ impl FileDescription for NullOutput {
204
229
& self ,
205
230
_self_ref : & FileDescriptionRef ,
206
231
_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 > {
210
237
// 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)
212
240
}
213
241
}
214
242
@@ -535,7 +563,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
535
563
buf : Pointer ,
536
564
count : u64 ,
537
565
offset : Option < i128 > ,
538
- ) -> InterpResult < ' tcx , Scalar > {
566
+ dest : & MPlaceTy < ' tcx > ,
567
+ ) -> InterpResult < ' tcx > {
539
568
let this = self . eval_context_mut ( ) ;
540
569
541
570
// Isolation check is done via `FileDescription` trait.
@@ -550,48 +579,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
550
579
let count = count
551
580
. min ( u64:: try_from ( this. target_isize_max ( ) ) . unwrap ( ) )
552
581
. min ( u64:: try_from ( isize:: MAX ) . unwrap ( ) ) ;
582
+ let count = usize:: try_from ( count) . unwrap ( ) ; // now it fits in a `usize`
553
583
let communicate = this. machine . communicate ( ) ;
554
584
555
585
// We temporarily dup the FD to be able to retain mutable access to `this`.
556
586
let Some ( fd) = this. machine . fds . get ( fd_num) else {
557
587
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 ( ( ) ) ;
559
591
} ;
560
592
561
593
trace ! ( "read: FD mapped to {fd:?}" ) ;
562
594
// We want to read at most `count` bytes. We are sure that `count` is not negative
563
595
// because it was a target's `usize`. Also we are sure that its smaller than
564
596
// `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) ? ,
568
600
Some ( offset) => {
569
601
let Ok ( offset) = u64:: try_from ( offset) else {
570
602
let einval = this. eval_libc ( "EINVAL" ) ;
571
603
this. set_last_error ( einval) ?;
572
- return Ok ( Scalar :: from_target_isize ( -1 , this) ) ;
604
+ this. write_int ( -1 , dest) ?;
605
+ return Ok ( ( ) ) ;
573
606
} ;
574
- fd. pread ( communicate, & mut bytes , offset , this)
607
+ fd. pread ( communicate, offset , buf , count , dest , this) ?
575
608
}
576
609
} ;
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 ( ( ) )
595
611
}
596
612
597
613
fn write (
@@ -600,7 +616,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
600
616
buf : Pointer ,
601
617
count : u64 ,
602
618
offset : Option < i128 > ,
603
- ) -> InterpResult < ' tcx , Scalar > {
619
+ dest : & MPlaceTy < ' tcx > ,
620
+ ) -> InterpResult < ' tcx > {
604
621
let this = self . eval_context_mut ( ) ;
605
622
606
623
// Isolation check is done via `FileDescription` trait.
@@ -613,27 +630,72 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
613
630
let count = count
614
631
. min ( u64:: try_from ( this. target_isize_max ( ) ) . unwrap ( ) )
615
632
. min ( u64:: try_from ( isize:: MAX ) . unwrap ( ) ) ;
633
+ let count = usize:: try_from ( count) . unwrap ( ) ; // now it fits in a `usize`
616
634
let communicate = this. machine . communicate ( ) ;
617
635
618
- let bytes = this. read_bytes_ptr_strip_provenance ( buf, Size :: from_bytes ( count) ) ?. to_owned ( ) ;
619
636
// We temporarily dup the FD to be able to retain mutable access to `this`.
620
637
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 ( ( ) ) ;
622
641
} ;
623
642
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) ? ,
626
645
Some ( offset) => {
627
646
let Ok ( offset) = u64:: try_from ( offset) else {
628
647
let einval = this. eval_libc ( "EINVAL" ) ;
629
648
this. set_last_error ( einval) ?;
630
- return Ok ( Scalar :: from_target_isize ( -1 , this) ) ;
649
+ this. write_int ( -1 , dest) ?;
650
+ return Ok ( ( ) ) ;
631
651
} ;
632
- fd. pwrite ( communicate, & bytes , offset, this)
652
+ fd. pwrite ( communicate, buf , count , offset, dest , this) ?
633
653
}
634
654
} ;
655
+ Ok ( ( ) )
656
+ }
635
657
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 ( ( ) )
638
700
}
639
701
}
0 commit comments