@@ -36,6 +36,30 @@ pub trait FileDescription: std::fmt::Debug + Any {
3636 throw_unsup_format ! ( "cannot write to {}" , self . name( ) ) ;
3737 }
3838
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+
3963 /// Seeks to the given offset (which can be relative to the beginning, end, or current position).
4064 /// Returns the new position from the start of the stream.
4165 fn seek < ' tcx > (
@@ -463,4 +487,116 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
463487
464488 this. try_unwrap_io_result ( result)
465489 }
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+ }
466602}
0 commit comments