@@ -36,6 +36,30 @@ pub trait FileDescription: std::fmt::Debug + Any {
36
36
throw_unsup_format ! ( "cannot write to {}" , self . name( ) ) ;
37
37
}
38
38
39
+ /// Reads as much as possible into the given buffer from a given offset,
40
+ /// and returns the number of bytes read.
41
+ fn pread < ' tcx > (
42
+ & mut self ,
43
+ _communicate_allowed : bool ,
44
+ _bytes : & mut [ u8 ] ,
45
+ _offset : u64 ,
46
+ _ecx : & mut MiriInterpCx < ' tcx > ,
47
+ ) -> InterpResult < ' tcx , io:: Result < usize > > {
48
+ throw_unsup_format ! ( "cannot pread from {}" , self . name( ) ) ;
49
+ }
50
+
51
+ /// Writes as much as possible from the given buffer starting at a given offset,
52
+ /// and returns the number of bytes written.
53
+ fn pwrite < ' tcx > (
54
+ & mut self ,
55
+ _communicate_allowed : bool ,
56
+ _bytes : & [ u8 ] ,
57
+ _offset : u64 ,
58
+ _ecx : & mut MiriInterpCx < ' tcx > ,
59
+ ) -> InterpResult < ' tcx , io:: Result < usize > > {
60
+ throw_unsup_format ! ( "cannot pwrite to {}" , self . name( ) ) ;
61
+ }
62
+
39
63
/// Seeks to the given offset (which can be relative to the beginning, end, or current position).
40
64
/// Returns the new position from the start of the stream.
41
65
fn seek < ' tcx > (
@@ -463,4 +487,116 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
463
487
464
488
this. try_unwrap_io_result ( result)
465
489
}
490
+
491
+ fn pread (
492
+ & mut self ,
493
+ fd : i32 ,
494
+ buf : Pointer ,
495
+ count : u64 ,
496
+ offset : i128 ,
497
+ ) -> InterpResult < ' tcx , i64 > {
498
+ let this = self . eval_context_mut ( ) ;
499
+
500
+ // Isolation check is done via `FileDescriptor` trait.
501
+
502
+ trace ! ( "Reading from FD {}, size {}, offset {}" , fd, count, offset) ;
503
+
504
+ // Check that the *entire* buffer is actually valid memory.
505
+ this. check_ptr_access ( buf, Size :: from_bytes ( count) , CheckInAllocMsg :: MemoryAccessTest ) ?;
506
+
507
+ // We cap the number of read bytes to the largest value that we are able to fit in both the
508
+ // host's and target's `isize`. This saves us from having to handle overflows later.
509
+ let count = count
510
+ . min ( u64:: try_from ( this. target_isize_max ( ) ) . unwrap ( ) )
511
+ . min ( u64:: try_from ( isize:: MAX ) . unwrap ( ) ) ;
512
+ let communicate = this. machine . communicate ( ) ;
513
+
514
+ // We temporarily dup the FD to be able to retain mutable access to `this`.
515
+ let Some ( file_descriptor) = this. machine . fds . dup ( fd) else {
516
+ trace ! ( "pread: FD not found" ) ;
517
+ return this. fd_not_found ( ) ;
518
+ } ;
519
+
520
+ trace ! ( "pread: FD mapped to {:?}" , file_descriptor) ;
521
+ // We want to read at most `count` bytes. We are sure that `count` is not negative
522
+ // because it was a target's `usize`. Also we are sure that its smaller than
523
+ // `usize::MAX` because it is bounded by the host's `isize`.
524
+ let mut bytes = vec ! [ 0 ; usize :: try_from( count) . unwrap( ) ] ;
525
+ let offset = match offset. try_into ( ) {
526
+ Ok ( offset) => offset,
527
+ Err ( _) => {
528
+ let einval = this. eval_libc ( "EINVAL" ) ;
529
+ this. set_last_error ( einval) ?;
530
+ return Ok ( -1 ) ;
531
+ }
532
+ } ;
533
+ // `File::pread` never returns a value larger than `count`,
534
+ // so this cannot fail.
535
+ let result = file_descriptor
536
+ . borrow_mut ( )
537
+ . pread ( communicate, & mut bytes, offset, this) ?
538
+ . map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
539
+ drop ( file_descriptor) ;
540
+
541
+ match result {
542
+ Ok ( read_bytes) => {
543
+ // If reading to `bytes` did not fail, we write those bytes to the buffer.
544
+ // Crucially, if fewer than `bytes.len()` bytes were read, only write
545
+ // that much into the output buffer!
546
+ this. write_bytes_ptr (
547
+ buf,
548
+ bytes[ ..usize:: try_from ( read_bytes) . unwrap ( ) ] . iter ( ) . copied ( ) ,
549
+ ) ?;
550
+ Ok ( read_bytes)
551
+ }
552
+ Err ( e) => {
553
+ this. set_last_error_from_io_error ( e) ?;
554
+ Ok ( -1 )
555
+ }
556
+ }
557
+ }
558
+
559
+ fn pwrite (
560
+ & mut self ,
561
+ fd : i32 ,
562
+ buf : Pointer ,
563
+ count : u64 ,
564
+ offset : i128 ,
565
+ ) -> InterpResult < ' tcx , i64 > {
566
+ let this = self . eval_context_mut ( ) ;
567
+
568
+ // Isolation check is done via `FileDescriptor` trait.
569
+
570
+ // Check that the *entire* buffer is actually valid memory.
571
+ this. check_ptr_access ( buf, Size :: from_bytes ( count) , CheckInAllocMsg :: MemoryAccessTest ) ?;
572
+
573
+ // We cap the number of written bytes to the largest value that we are able to fit in both the
574
+ // host's and target's `isize`. This saves us from having to handle overflows later.
575
+ let count = count
576
+ . min ( u64:: try_from ( this. target_isize_max ( ) ) . unwrap ( ) )
577
+ . min ( u64:: try_from ( isize:: MAX ) . unwrap ( ) ) ;
578
+ let communicate = this. machine . communicate ( ) ;
579
+
580
+ let bytes = this. read_bytes_ptr_strip_provenance ( buf, Size :: from_bytes ( count) ) ?. to_owned ( ) ;
581
+ let offset = match offset. try_into ( ) {
582
+ Ok ( offset) => offset,
583
+ Err ( _) => {
584
+ let einval = this. eval_libc ( "EINVAL" ) ;
585
+ this. set_last_error ( einval) ?;
586
+ return Ok ( -1 ) ;
587
+ }
588
+ } ;
589
+ // We temporarily dup the FD to be able to retain mutable access to `this`.
590
+ let Some ( file_descriptor) = this. machine . fds . dup ( fd) else {
591
+ return this. fd_not_found ( ) ;
592
+ } ;
593
+
594
+ let result = file_descriptor
595
+ . borrow_mut ( )
596
+ . pwrite ( communicate, & bytes, offset, this) ?
597
+ . map ( |c| i64:: try_from ( c) . unwrap ( ) ) ;
598
+ drop ( file_descriptor) ;
599
+
600
+ this. try_unwrap_io_result ( result)
601
+ }
466
602
}
0 commit comments