From: Samrat Man Singh Date: Sat, 1 Aug 2020 11:38:28 +0000 (+0530) Subject: Add `impl FileDescriptor` for stdin, stdout, stderr X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=bea7113eb8b3ce63b95a9c6a4f8d39c7f912d9e1;p=rust.git Add `impl FileDescriptor` for stdin, stdout, stderr - Use `FileDescriptor::read` for stdin reads - Use `FileDescriptor::write` for stdout/err writes - Handle stdout/err reads in `FileDescriptor::read` --- diff --git a/src/shims/posix/foreign_items.rs b/src/shims/posix/foreign_items.rs index 4bb94ae8944..594f58d2646 100644 --- a/src/shims/posix/foreign_items.rs +++ b/src/shims/posix/foreign_items.rs @@ -1,5 +1,4 @@ -use std::convert::TryFrom; -use std::io::{self, Read, Write}; +use std::io::{self, Write}; use log::trace; @@ -67,43 +66,7 @@ fn emulate_foreign_item_by_name( let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_scalar(buf)?.check_init()?; let count = this.read_scalar(count)?.to_machine_usize(this)?; - let result = if fd == 0 { - - this.check_no_isolation("read")?; - - // We cap the number of read bytes to the largest - // value that we are able to fit in both the - // host's and target's `isize`. This saves us from - // having to handle overflows later. - let count = count.min(this.machine_isize_max() as u64).min(isize::MAX as u64); - - // We want to read at most `count` bytes. We are - // sure that `count` is not negative because it - // was a target's `usize`. Also we are sure that - // its smaller than `usize::MAX` because it is a - // host's `isize`. - let mut buffer = vec![0; count as usize]; - let res = io::stdin() - .read(&mut buffer) - // `Stdin::read` never returns a value larger - // than `count`, so this cannot fail. - .map(|c| i64::try_from(c).unwrap()); - - match res { - Ok(bytes) => { - this.memory.write_bytes(buf, buffer)?; - i64::try_from(bytes).unwrap() - }, - Err(e) => { - this.set_last_error_from_io_error(e)?; - -1 - }, - } - } else if fd == 1 || fd == 2 { - throw_unsup_format!("cannot read from stdout/stderr") - } else { - this.read(fd, buf, count)? - }; + let result = this.read(fd, buf, count)?; this.write_scalar(Scalar::from_machine_isize(result, this), dest)?; } "write" => { @@ -112,35 +75,10 @@ fn emulate_foreign_item_by_name( let buf = this.read_scalar(buf)?.check_init()?; let count = this.read_scalar(n)?.to_machine_usize(this)?; trace!("Called write({:?}, {:?}, {:?})", fd, buf, count); - let result = if fd == 0 { - throw_unsup_format!("cannot write to stdin") - } else if fd == 1 || fd == 2 { - // stdout/stderr - - let buf_cont = this.memory.read_bytes(buf, Size::from_bytes(count))?; - // We need to flush to make sure this actually appears on the screen - let res = if fd == 1 { - // Stdout is buffered, flush to make sure it appears on the screen. - // This is the write() syscall of the interpreted program, we want it - // to correspond to a write() syscall on the host -- there is no good - // in adding extra buffering here. - let res = io::stdout().write(buf_cont); - io::stdout().flush().unwrap(); - res - } else { - // No need to flush, stderr is not buffered. - io::stderr().write(buf_cont) - }; - match res { - Ok(n) => i64::try_from(n).unwrap(), - Err(e) => { - this.set_last_error_from_io_error(e)?; - -1 - } - } - } else { - this.write(fd, buf, count)? - }; + let result = this.write(fd, buf, count)?; + if fd == 1 { + io::stdout().flush().unwrap(); + } // Now, `result` is the value we return back to the program. this.write_scalar(Scalar::from_machine_isize(result, this), dest)?; } diff --git a/src/shims/posix/fs.rs b/src/shims/posix/fs.rs index 2d784490656..65d50aa504d 100644 --- a/src/shims/posix/fs.rs +++ b/src/shims/posix/fs.rs @@ -48,11 +48,77 @@ fn seek(&mut self, offset: SeekFrom) -> InterpResult<'tcx, io::Result> { } } -#[derive(Debug, Default)] +impl<'tcx> FileDescriptor<'tcx> for io::Stdin { + fn as_file_handle(&self) -> InterpResult<'tcx, &FileHandle> { + throw_unsup_format!("stdin cannot be used as FileHandle"); + } + + fn read(&mut self, bytes: &mut [u8]) -> InterpResult<'tcx, io::Result> { + Ok(Read::read(self, bytes)) + } + + fn write(&mut self, _bytes: &[u8]) -> InterpResult<'tcx, io::Result> { + throw_unsup_format!("cannot write to stdin"); + } + + fn seek(&mut self, _offset: SeekFrom) -> InterpResult<'tcx, io::Result> { + throw_unsup_format!("cannot seek on stdin"); + } +} + +impl<'tcx> FileDescriptor<'tcx> for io::Stdout { + fn as_file_handle(&self) -> InterpResult<'tcx, &FileHandle> { + throw_unsup_format!("stdout cannot be used as FileHandle"); + } + + fn read(&mut self, _bytes: &mut [u8]) -> InterpResult<'tcx, io::Result> { + throw_unsup_format!("cannot read from stdout"); + } + + fn write(&mut self, bytes: &[u8]) -> InterpResult<'tcx, io::Result> { + Ok(Write::write(self, bytes)) + } + + fn seek(&mut self, _offset: SeekFrom) -> InterpResult<'tcx, io::Result> { + throw_unsup_format!("cannot seek on stdout"); + } +} + +impl<'tcx> FileDescriptor<'tcx> for io::Stderr { + fn as_file_handle(&self) -> InterpResult<'tcx, &FileHandle> { + throw_unsup_format!("stdout cannot be used as FileHandle"); + } + + fn read(&mut self, _bytes: &mut [u8]) -> InterpResult<'tcx, io::Result> { + throw_unsup_format!("cannot read from stderr"); + } + + fn write(&mut self, bytes: &[u8]) -> InterpResult<'tcx, io::Result> { + Ok(Write::write(self, bytes)) + } + + fn seek(&mut self, _offset: SeekFrom) -> InterpResult<'tcx, io::Result> { + throw_unsup_format!("cannot seek on stderr"); + } +} + +#[derive(Debug)] pub struct FileHandler<'tcx> { handles: BTreeMap>>, } +impl<'tcx> Default for FileHandler<'tcx> { + fn default() -> Self { + let mut handles = BTreeMap::new(); + handles.insert(0i32, Box::new(io::stdin()) as Box>); + handles.insert(1i32, Box::new(io::stdout()) as Box>); + handles.insert(2i32, Box::new(io::stderr()) as Box>); + FileHandler { + handles + } + } +} + // fd numbers 0, 1, and 2 are reserved for stdin, stdout, and stderr const MIN_NORMAL_FILE_FD: i32 = 3; @@ -485,7 +551,6 @@ fn read( let this = self.eval_context_mut(); this.check_no_isolation("read")?; - assert!(fd >= 3); trace!("Reading from FD {}, size {}", fd, count); @@ -537,8 +602,9 @@ fn write( ) -> InterpResult<'tcx, i64> { let this = self.eval_context_mut(); - this.check_no_isolation("write")?; - assert!(fd >= 3); + if fd >= 3 { + this.check_no_isolation("write")?; + } // Check that the *entire* buffer is actually valid memory. this.memory.check_ptr_access( diff --git a/tests/compile-fail/fs/read_from_stdout.rs b/tests/compile-fail/fs/read_from_stdout.rs new file mode 100644 index 00000000000..17f1735f6ad --- /dev/null +++ b/tests/compile-fail/fs/read_from_stdout.rs @@ -0,0 +1,14 @@ +// compile-flags: -Zmiri-disable-isolation +// ignore-windows: No libc on Windows + +#![feature(rustc_private)] + +extern crate libc; + +fn main() -> std::io::Result<()> { + let mut bytes = [0u8; 512]; + unsafe { + libc::read(1, bytes.as_mut_ptr() as *mut libc::c_void, 512); //~ ERROR cannot read from stdout + } + Ok(()) +} diff --git a/tests/compile-fail/fs/write_to_stdin.rs b/tests/compile-fail/fs/write_to_stdin.rs new file mode 100644 index 00000000000..30d24b5dc44 --- /dev/null +++ b/tests/compile-fail/fs/write_to_stdin.rs @@ -0,0 +1,14 @@ +// compile-flags: -Zmiri-disable-isolation +// ignore-windows: No libc on Windows + +#![feature(rustc_private)] + +extern crate libc; + +fn main() -> std::io::Result<()> { + let bytes = b"hello"; + unsafe { + libc::write(0, bytes.as_ptr() as *const libc::c_void, 5); //~ ERROR cannot write to stdin + } + Ok(()) +}