]> git.lizzy.rs Git - rust.git/commitdiff
Add `impl FileDescriptor` for stdin, stdout, stderr
authorSamrat Man Singh <samratmansingh@gmail.com>
Sat, 1 Aug 2020 11:38:28 +0000 (17:08 +0530)
committerSamrat Man Singh <samratmansingh@gmail.com>
Mon, 3 Aug 2020 19:44:52 +0000 (01:14 +0530)
- Use `FileDescriptor::read` for stdin reads
- Use `FileDescriptor::write` for stdout/err writes
- Handle stdout/err reads in `FileDescriptor::read`

src/shims/posix/foreign_items.rs
src/shims/posix/fs.rs
tests/compile-fail/fs/read_from_stdout.rs [new file with mode: 0644]
tests/compile-fail/fs/write_to_stdin.rs [new file with mode: 0644]

index 4bb94ae89449a6166351eaef621b2a8f9ff197f9..594f58d26461f9cddb9c51004b2787cc9727e5ee 100644 (file)
@@ -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)?;
             }
index 2d784490656c9fbcfb176e80a3b4cd8c803251c3..65d50aa504d3d6b0f3d5c444c496e93144998194 100644 (file)
@@ -48,11 +48,77 @@ fn seek(&mut self, offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
     }
 }
 
-#[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<usize>> {
+        Ok(Read::read(self, bytes))
+    }
+
+    fn write(&mut self, _bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>> {
+        throw_unsup_format!("cannot write to stdin");
+    }
+
+    fn seek(&mut self, _offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
+        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<usize>> {
+        throw_unsup_format!("cannot read from stdout");
+    }
+
+    fn write(&mut self, bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>> {
+        Ok(Write::write(self, bytes))
+    }
+
+    fn seek(&mut self, _offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
+        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<usize>> {
+        throw_unsup_format!("cannot read from stderr");
+    }
+
+    fn write(&mut self, bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>> {
+        Ok(Write::write(self, bytes))
+    }
+
+    fn seek(&mut self, _offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
+        throw_unsup_format!("cannot seek on stderr");
+    }
+}
+
+#[derive(Debug)]
 pub struct FileHandler<'tcx> {
     handles: BTreeMap<i32, Box<dyn FileDescriptor<'tcx>>>,
 }
 
+impl<'tcx> Default for FileHandler<'tcx> {
+    fn default() -> Self {
+        let mut handles = BTreeMap::new();
+        handles.insert(0i32, Box::new(io::stdin()) as Box<dyn FileDescriptor<'_>>);
+        handles.insert(1i32, Box::new(io::stdout()) as Box<dyn FileDescriptor<'_>>);
+        handles.insert(2i32, Box::new(io::stderr()) as Box<dyn FileDescriptor<'_>>);
+        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 (file)
index 0000000..17f1735
--- /dev/null
@@ -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 (file)
index 0000000..30d24b5
--- /dev/null
@@ -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(())
+}