about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/miri/src/lib.rs1
-rw-r--r--src/tools/miri/src/shims/unix/foreign_items.rs2
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs68
3 files changed, 41 insertions, 30 deletions
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 479353bb983..97271c33a2e 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -9,6 +9,7 @@
 #![feature(is_some_and)]
 #![feature(nonzero_ops)]
 #![feature(local_key_cell_methods)]
+#![feature(is_terminal)]
 // Configure clippy and other lints
 #![allow(
     clippy::collapsible_else_if,
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index c21e0441cac..44a433df1e9 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -452,7 +452,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             "isatty" => {
                 let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 let result = this.isatty(fd)?;
-                this.write_scalar(Scalar::from_i32(result), dest)?;
+                this.write_scalar(result, dest)?;
             }
             "pthread_atfork" => {
                 let [prepare, parent, child] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index ed68976773d..0610f65db11 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -4,7 +4,7 @@ use std::convert::TryInto;
 use std::fs::{
     read_dir, remove_dir, remove_file, rename, DirBuilder, File, FileType, OpenOptions, ReadDir,
 };
-use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write};
+use std::io::{self, ErrorKind, IsTerminal, Read, Seek, SeekFrom, Write};
 use std::path::{Path, PathBuf};
 use std::time::SystemTime;
 
@@ -65,6 +65,8 @@ trait FileDescriptor: std::fmt::Debug {
 
     fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>>;
 
+    fn is_tty(&self) -> bool;
+
     #[cfg(unix)]
     fn as_unix_host_fd(&self) -> Option<i32> {
         None
@@ -143,6 +145,10 @@ impl FileDescriptor for FileHandle {
         use std::os::unix::io::AsRawFd;
         Some(self.file.as_raw_fd())
     }
+
+    fn is_tty(&self) -> bool {
+        self.file.is_terminal()
+    }
 }
 
 impl FileDescriptor for io::Stdin {
@@ -170,6 +176,10 @@ impl FileDescriptor for io::Stdin {
     fn as_unix_host_fd(&self) -> Option<i32> {
         Some(libc::STDIN_FILENO)
     }
+
+    fn is_tty(&self) -> bool {
+        self.is_terminal()
+    }
 }
 
 impl FileDescriptor for io::Stdout {
@@ -202,6 +212,10 @@ impl FileDescriptor for io::Stdout {
     fn as_unix_host_fd(&self) -> Option<i32> {
         Some(libc::STDOUT_FILENO)
     }
+
+    fn is_tty(&self) -> bool {
+        self.is_terminal()
+    }
 }
 
 impl FileDescriptor for io::Stderr {
@@ -227,12 +241,16 @@ impl FileDescriptor for io::Stderr {
     fn as_unix_host_fd(&self) -> Option<i32> {
         Some(libc::STDERR_FILENO)
     }
+
+    fn is_tty(&self) -> bool {
+        self.is_terminal()
+    }
 }
 
 #[derive(Debug)]
-struct DummyOutput;
+struct NullOutput;
 
-impl FileDescriptor for DummyOutput {
+impl FileDescriptor for NullOutput {
     fn name(&self) -> &'static str {
         "stderr and stdout"
     }
@@ -247,7 +265,11 @@ impl FileDescriptor for DummyOutput {
     }
 
     fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
-        Ok(Box::new(DummyOutput))
+        Ok(Box::new(NullOutput))
+    }
+
+    fn is_tty(&self) -> bool {
+        false
     }
 }
 
@@ -267,8 +289,8 @@ impl FileHandler {
         let mut handles: BTreeMap<_, Box<dyn FileDescriptor>> = BTreeMap::new();
         handles.insert(0i32, Box::new(io::stdin()));
         if mute_stdout_stderr {
-            handles.insert(1i32, Box::new(DummyOutput));
-            handles.insert(2i32, Box::new(DummyOutput));
+            handles.insert(1i32, Box::new(NullOutput));
+            handles.insert(2i32, Box::new(NullOutput));
         } else {
             handles.insert(1i32, Box::new(io::stdout()));
             handles.insert(2i32, Box::new(io::stderr()));
@@ -1662,35 +1684,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     }
 
     #[cfg_attr(not(unix), allow(unused))]
-    fn isatty(&mut self, miri_fd: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
+    fn isatty(
+        &mut self,
+        miri_fd: &OpTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, Scalar<Provenance>> {
         let this = self.eval_context_mut();
-        #[cfg(unix)]
+        // "returns 1 if fd is an open file descriptor referring to a terminal;
+        // otherwise 0 is returned, and errno is set to indicate the error"
         if matches!(this.machine.isolated_op, IsolatedOp::Allow) {
-            let miri_fd = this.read_scalar(miri_fd)?.to_i32()?;
-            if let Some(host_fd) =
-                this.machine.file_handler.handles.get(&miri_fd).and_then(|fd| fd.as_unix_host_fd())
-            {
-                // "returns 1 if fd is an open file descriptor referring to a terminal;
-                // otherwise 0 is returned, and errno is set to indicate the error"
-                // SAFETY: isatty has no preconditions
-                let is_tty = unsafe { libc::isatty(host_fd) };
-                if is_tty == 0 {
-                    let errno = std::io::Error::last_os_error()
-                        .raw_os_error()
-                        .map(Scalar::from_i32)
-                        .unwrap();
-                    this.set_last_error(errno)?;
-                }
-                return Ok(is_tty);
+            let fd = this.read_scalar(miri_fd)?.to_i32()?;
+            if this.machine.file_handler.handles.get(&fd).map(|fd| fd.is_tty()) == Some(true) {
+                return Ok(Scalar::from_i32(1));
             }
         }
-        // We are attemping to use a Unix interface on a non-Unix platform, or we are on a Unix
-        // platform and the passed file descriptor is not open, or isolation is enabled
-        // FIXME: It should be possible to emulate this at least on Windows by using
-        // GetConsoleMode.
+        // Fallback when the FD was not found or isolation is enabled.
         let enotty = this.eval_libc("ENOTTY")?;
         this.set_last_error(enotty)?;
-        Ok(0)
+        Ok(Scalar::from_i32(0))
     }
 
     fn realpath(