]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #1498 - RalfJung:rustup, r=RalfJung
authorbors <bors@rust-lang.org>
Wed, 5 Aug 2020 11:39:07 +0000 (11:39 +0000)
committerbors <bors@rust-lang.org>
Wed, 5 Aug 2020 11:39:07 +0000 (11:39 +0000)
rustup

The allocator API changed *again*, adjust our test.

src/machine.rs
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 5dfe99627437df5da938d6e9e5971a082bb6c075..f9dd48fdbae2adc07d696da157c57b49321a1e72 100644 (file)
@@ -250,7 +250,7 @@ pub struct Evaluator<'mir, 'tcx> {
     /// Whether to enforce the validity invariant.
     pub(crate) validate: bool,
 
-    pub(crate) file_handler: shims::posix::FileHandler,
+    pub(crate) file_handler: shims::posix::FileHandler<'tcx>,
     pub(crate) dir_handler: shims::posix::DirHandler,
 
     /// The temporary used for storing the argument of
index 4bb94ae89449a6166351eaef621b2a8f9ff197f9..151ab95f1e3c4d278d89a0972ce9ec0b2cda9350 100644 (file)
@@ -1,6 +1,3 @@
-use std::convert::TryFrom;
-use std::io::{self, Read, Write};
-
 use log::trace;
 
 use rustc_middle::mir;
@@ -67,43 +64,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 +73,7 @@ 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)?;
                 // Now, `result` is the value we return back to the program.
                 this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
             }
index a50228a4847c6763452d31c1b91f19f9878a5717..3e1ba3976f776a783fce15a674c6f8facbf03b69 100644 (file)
@@ -1,7 +1,7 @@
 use std::collections::BTreeMap;
 use std::convert::{TryFrom, TryInto};
 use std::fs::{read_dir, remove_dir, remove_file, rename, DirBuilder, File, FileType, OpenOptions, ReadDir};
-use std::io::{Read, Seek, SeekFrom, Write};
+use std::io::{self, Read, Seek, SeekFrom, Write};
 use std::path::Path;
 use std::time::SystemTime;
 
@@ -22,15 +22,116 @@ struct FileHandle {
     writable: bool,
 }
 
-#[derive(Debug, Default)]
-pub struct FileHandler {
-    handles: BTreeMap<i32, FileHandle>,
+trait FileDescriptor<'tcx> : std::fmt::Debug {
+    fn as_file_handle(&self) -> InterpResult<'tcx, &FileHandle>;
+
+    fn read(&mut self, bytes: &mut [u8]) -> InterpResult<'tcx, io::Result<usize>>;
+    fn write(&mut self, bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>>;
+    fn seek(&mut self, offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>>;
+}
+
+impl<'tcx> FileDescriptor<'tcx> for FileHandle {
+    fn as_file_handle(&self) -> InterpResult<'tcx, &FileHandle> {
+        Ok(&self)
+    }
+
+    fn read(&mut self, bytes: &mut [u8]) -> InterpResult<'tcx, io::Result<usize>> {
+        Ok(self.file.read(bytes))
+    }
+
+    fn write(&mut self, bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>> {
+        Ok(self.file.write(bytes))
+    }
+
+    fn seek(&mut self, offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
+        Ok(self.file.seek(offset))
+    }
+}
+
+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>> {
+        let result = Write::write(self, bytes);
+        // 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.
+        io::stdout().flush().unwrap();
+
+        Ok(result)
+    }
+
+    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;
 
-impl FileHandler {
+impl<'tcx> FileHandler<'tcx> {
     fn insert_fd(&mut self, file_handle: FileHandle) -> i32 {
         self.insert_fd_with_min_fd(file_handle, 0)
     }
@@ -62,7 +163,7 @@ fn insert_fd_with_min_fd(&mut self, file_handle: FileHandle, min_fd: i32) -> i32
             self.handles.last_key_value().map(|(fd, _)| fd.checked_add(1).unwrap()).unwrap_or(min_fd)
         });
 
-        self.handles.insert(new_fd, file_handle).unwrap_none();
+        self.handles.insert(new_fd, Box::new(file_handle)).unwrap_none();
         new_fd
     }
 }
@@ -383,7 +484,10 @@ fn fcntl(
             }
             let fh = &mut this.machine.file_handler;
             let (file_result, writable) = match fh.handles.get(&fd) {
-                Some(FileHandle { file, writable }) => (file.try_clone(), *writable),
+                Some(file_descriptor) => match file_descriptor.as_file_handle() {
+                    Ok(FileHandle { file, writable }) => (file.try_clone(), *writable),
+                    Err(_) => return this.handle_not_found(),
+                },
                 None => return this.handle_not_found(),
             };
             let fd_result = file_result.map(|duplicated| {
@@ -394,9 +498,14 @@ fn fcntl(
             && cmd == this.eval_libc_i32("F_FULLFSYNC")?
         {
             let &[_, _] = check_arg_count(args)?;
-            if let Some(FileHandle { file, writable }) = this.machine.file_handler.handles.get(&fd) {
-                let io_result = maybe_sync_file(file, *writable, File::sync_all);
-                this.try_unwrap_io_result(io_result)
+            if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) {
+                match file_descriptor.as_file_handle() {
+                    Ok(FileHandle { file, writable }) => {
+                        let io_result = maybe_sync_file(&file, *writable, File::sync_all);
+                        this.try_unwrap_io_result(io_result)
+                    },
+                    Err(_) => this.handle_not_found(),
+                }
             } else {
                 this.handle_not_found()
             }
@@ -412,24 +521,29 @@ fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
 
         let fd = this.read_scalar(fd_op)?.to_i32()?;
 
-        if let Some(FileHandle { file, writable }) = this.machine.file_handler.handles.remove(&fd) {
-            // We sync the file if it was opened in a mode different than read-only.
-            if writable {
-                // `File::sync_all` does the checks that are done when closing a file. We do this to
-                // to handle possible errors correctly.
-                let result = this.try_unwrap_io_result(file.sync_all().map(|_| 0i32));
-                // Now we actually close the file.
-                drop(file);
-                // And return the result.
-                result
-            } else {
-                // We drop the file, this closes it but ignores any errors produced when closing
-                // it. This is done because `File::sync_all` cannot be done over files like
-                // `/dev/urandom` which are read-only. Check
-                // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439 for a deeper
-                // discussion.
-                drop(file);
-                Ok(0)
+        if let Some(file_descriptor) = this.machine.file_handler.handles.remove(&fd) {
+            match file_descriptor.as_file_handle() {
+                Ok(FileHandle { file, writable }) => {
+                    // We sync the file if it was opened in a mode different than read-only.
+                    if *writable {
+                        // `File::sync_all` does the checks that are done when closing a file. We do this to
+                        // to handle possible errors correctly.
+                        let result = this.try_unwrap_io_result(file.sync_all().map(|_| 0i32));
+                        // Now we actually close the file.
+                        drop(file);
+                        // And return the result.
+                        result
+                    } else {
+                        // We drop the file, this closes it but ignores any errors produced when closing
+                        // it. This is done because `File::sync_all` cannot be done over files like
+                        // `/dev/urandom` which are read-only. Check
+                        // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439 for a deeper
+                        // discussion.
+                        drop(file);
+                        Ok(0)
+                    }
+                },
+                Err(_) => this.handle_not_found()
             }
         } else {
             this.handle_not_found()
@@ -445,7 +559,6 @@ fn read(
         let this = self.eval_context_mut();
 
         this.check_no_isolation("read")?;
-        assert!(fd >= 3);
 
         trace!("Reading from FD {}, size {}", fd, count);
 
@@ -460,15 +573,16 @@ fn read(
         // 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);
 
-        if let Some(FileHandle { file, writable: _ }) = this.machine.file_handler.handles.get_mut(&fd) {
-            trace!("read: FD mapped to {:?}", file);
+        if let Some(file_descriptor) = this.machine.file_handler.handles.get_mut(&fd) {
+            trace!("read: FD mapped to {:?}", file_descriptor);
             // 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 bytes = vec![0; count as usize];
-            let result = file
-                .read(&mut bytes)
-                // `File::read` never returns a value larger than `count`, so this cannot fail.
+            // `File::read` never returns a value larger than `count`,
+            // so this cannot fail.
+            let result = file_descriptor
+                .read(&mut bytes)?
                 .map(|c| i64::try_from(c).unwrap());
 
             match result {
@@ -496,8 +610,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(
@@ -510,9 +625,11 @@ fn write(
         // 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);
 
-        if let Some(FileHandle { file, writable: _ }) = this.machine.file_handler.handles.get_mut(&fd) {
+        if let Some(file_descriptor) = this.machine.file_handler.handles.get_mut(&fd) {
             let bytes = this.memory.read_bytes(buf, Size::from_bytes(count))?;
-            let result = file.write(&bytes).map(|c| i64::try_from(c).unwrap());
+            let result = file_descriptor
+                .write(&bytes)?
+                .map(|c| i64::try_from(c).unwrap());
             this.try_unwrap_io_result(result)
         } else {
             this.handle_not_found()
@@ -545,8 +662,10 @@ fn lseek64(
             return Ok(-1);
         };
 
-        if let Some(FileHandle { file, writable: _ }) = this.machine.file_handler.handles.get_mut(&fd) {
-            let result = file.seek(seek_from).map(|offset| i64::try_from(offset).unwrap());
+        if let Some(file_descriptor) = this.machine.file_handler.handles.get_mut(&fd) {
+            let result = file_descriptor
+                .seek(seek_from)?
+                .map(|offset| i64::try_from(offset).unwrap());
             this.try_unwrap_io_result(result)
         } else {
             this.handle_not_found()
@@ -1103,21 +1222,26 @@ fn ftruncate64(
 
         let fd = this.read_scalar(fd_op)?.to_i32()?;
         let length = this.read_scalar(length_op)?.to_i64()?;
-        if let Some(FileHandle { file, writable }) = this.machine.file_handler.handles.get_mut(&fd) {
-            if *writable {
-                if let Ok(length) = length.try_into() {
-                    let result = file.set_len(length);
-                    this.try_unwrap_io_result(result.map(|_| 0i32))
-                } else {
-                    let einval = this.eval_libc("EINVAL")?;
-                    this.set_last_error(einval)?;
-                    Ok(-1)
+        if let Some(file_descriptor) = this.machine.file_handler.handles.get_mut(&fd) {
+            match file_descriptor.as_file_handle() {
+                Ok(FileHandle { file, writable }) => {
+                    if *writable {
+                        if let Ok(length) = length.try_into() {
+                            let result = file.set_len(length);
+                            this.try_unwrap_io_result(result.map(|_| 0i32))
+                        } else {
+                            let einval = this.eval_libc("EINVAL")?;
+                            this.set_last_error(einval)?;
+                            Ok(-1)
+                        }
+                    } else {
+                        // The file is not writable
+                        let einval = this.eval_libc("EINVAL")?;
+                        this.set_last_error(einval)?;
+                        Ok(-1)
+                    }
                 }
-            } else {
-                // The file is not writable
-                let einval = this.eval_libc("EINVAL")?;
-                this.set_last_error(einval)?;
-                Ok(-1)
+                Err(_) => this.handle_not_found()
             }
         } else {
             this.handle_not_found()
@@ -1135,9 +1259,14 @@ fn fsync(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
         this.check_no_isolation("fsync")?;
 
         let fd = this.read_scalar(fd_op)?.to_i32()?;
-        if let Some(FileHandle { file, writable }) = this.machine.file_handler.handles.get(&fd) {
-            let io_result = maybe_sync_file(file, *writable, File::sync_all);
-            this.try_unwrap_io_result(io_result)
+        if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) {
+            match file_descriptor.as_file_handle() {
+                Ok(FileHandle { file, writable }) => {
+                    let io_result = maybe_sync_file(&file, *writable, File::sync_all);
+                    this.try_unwrap_io_result(io_result)
+                }
+                Err(_) => this.handle_not_found()
+            }
         } else {
             this.handle_not_found()
         }
@@ -1149,9 +1278,14 @@ fn fdatasync(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
         this.check_no_isolation("fdatasync")?;
 
         let fd = this.read_scalar(fd_op)?.to_i32()?;
-        if let Some(FileHandle { file, writable }) = this.machine.file_handler.handles.get(&fd) {
-            let io_result = maybe_sync_file(file, *writable, File::sync_data);
-            this.try_unwrap_io_result(io_result)
+        if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) {
+            match file_descriptor.as_file_handle() {
+                Ok(FileHandle { file, writable }) => {
+                    let io_result = maybe_sync_file(&file, *writable, File::sync_data);
+                    this.try_unwrap_io_result(io_result)
+                }
+                Err(_) => this.handle_not_found()
+            }
         } else {
             this.handle_not_found()
         }
@@ -1187,9 +1321,14 @@ fn sync_file_range(
             return Ok(-1);
         }
 
-        if let Some(FileHandle { file, writable }) = this.machine.file_handler.handles.get(&fd) {
-            let io_result = maybe_sync_file(file, *writable, File::sync_data);
-            this.try_unwrap_io_result(io_result)
+        if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) {
+            match file_descriptor.as_file_handle() {
+                Ok(FileHandle { file, writable }) => {
+                    let io_result = maybe_sync_file(&file, *writable, File::sync_data);
+                    this.try_unwrap_io_result(io_result)
+                },
+                Err(_) => this.handle_not_found()
+            }
         } else {
             this.handle_not_found()
         }
@@ -1239,7 +1378,10 @@ fn from_fd<'tcx, 'mir>(
     ) -> InterpResult<'tcx, Option<FileMetadata>> {
         let option = ecx.machine.file_handler.handles.get(&fd);
         let file = match option {
-            Some(FileHandle { file, writable: _ }) => file,
+            Some(file_descriptor) => match file_descriptor.as_file_handle() {
+                Ok(FileHandle { file, writable: _ }) => file,
+                Err(_) => return ecx.handle_not_found().map(|_: i32| None),
+            },
             None => return ecx.handle_not_found().map(|_: i32| None),
         };
         let metadata = file.metadata();
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..c275463
--- /dev/null
@@ -0,0 +1,13 @@
+// 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(())
+}