about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDebugSteven <debugsteven@gmail.com>2022-07-11 09:29:54 -0600
committerDebugSteven <debugsteven@gmail.com>2022-12-13 14:04:30 -0700
commit1b51f372eb42de22676ff88fa4d8cc02dab48218 (patch)
tree7cc26ffb30628c75d9edab901f00c8c1109a5976
parent8b19af05823a5a78241ffa792621510047a4f572 (diff)
downloadrust-1b51f372eb42de22676ff88fa4d8cc02dab48218.tar.gz
rust-1b51f372eb42de22676ff88fa4d8cc02dab48218.zip
implement minimal epoll_create1 shim
-rw-r--r--src/tools/miri/src/lib.rs1
-rw-r--r--src/tools/miri/src/machine.rs4
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs35
-rw-r--r--src/tools/miri/src/shims/unix/linux/fd.rs191
-rw-r--r--src/tools/miri/src/shims/unix/linux/fd/epoll.rs53
-rw-r--r--src/tools/miri/src/shims/unix/linux/fd/event.rs38
-rw-r--r--src/tools/miri/src/shims/unix/linux/fd/socketpair.rs32
-rw-r--r--src/tools/miri/src/shims/unix/linux/foreign_items.rs31
-rw-r--r--src/tools/miri/src/shims/unix/linux/mod.rs1
-rw-r--r--src/tools/miri/tests/fail/crates/tokio_mvp.rs7
-rw-r--r--src/tools/miri/tests/fail/crates/tokio_mvp.stderr19
-rw-r--r--src/tools/miri/tests/pass-dep/tokio_mvp.rs6
12 files changed, 377 insertions, 41 deletions
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index d65bd10e6ff..97750cb78cd 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -27,6 +27,7 @@
     clippy::single_element_loop,
     clippy::needless_return,
     clippy::bool_to_int_with_if,
+    clippy::box_default,
     // We are not implementing queries here so it's fine
     rustc::potential_query_instability
 )]
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 72f71db34b6..a7a61a1f44c 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -31,6 +31,10 @@ use crate::{
     *,
 };
 
+/// The number of the available real-time signal with the lowest priority.
+/// Dummy constant related to epoll, must be between 32 and 64.
+pub const SIGRTMAX: i32 = 42;
+
 /// Extra data stored with each stack frame
 pub struct FrameExtra<'tcx> {
     /// Extra data for Stacked Borrows.
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index ce2b0143b56..c46506e20ac 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -17,20 +17,25 @@ use crate::shims::os_str::bytes_to_os_str;
 use crate::*;
 use shims::os_str::os_str_to_bytes;
 use shims::time::system_time_to_duration;
+use shims::unix::linux::fd::epoll::Epoll;
 
 #[derive(Debug)]
-struct FileHandle {
+pub struct FileHandle {
     file: File,
     writable: bool,
 }
 
-trait FileDescriptor: std::fmt::Debug {
+pub trait FileDescriptor: std::fmt::Debug {
     fn name(&self) -> &'static str;
 
     fn as_file_handle<'tcx>(&self) -> InterpResult<'tcx, &FileHandle> {
         throw_unsup_format!("{} cannot be used as FileHandle", self.name());
     }
 
+    fn as_epoll_handle<'tcx>(&mut self) -> InterpResult<'tcx, &mut Epoll> {
+        throw_unsup_format!("not an epoll file descriptor");
+    }
+
     fn read<'tcx>(
         &mut self,
         _communicate_allowed: bool,
@@ -274,7 +279,7 @@ impl FileDescriptor for NullOutput {
 
 #[derive(Debug)]
 pub struct FileHandler {
-    handles: BTreeMap<i32, Box<dyn FileDescriptor>>,
+    pub handles: BTreeMap<i32, Box<dyn FileDescriptor>>,
 }
 
 impl VisitTags for FileHandler {
@@ -297,7 +302,7 @@ impl FileHandler {
         FileHandler { handles }
     }
 
-    fn insert_fd(&mut self, file_handle: Box<dyn FileDescriptor>) -> i32 {
+    pub fn insert_fd(&mut self, file_handle: Box<dyn FileDescriptor>) -> i32 {
         self.insert_fd_with_min_fd(file_handle, 0)
     }
 
@@ -376,17 +381,6 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx
         Ok(0)
     }
 
-    /// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
-    /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
-    /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
-    /// types (like `read`, that returns an `i64`).
-    fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
-        let this = self.eval_context_mut();
-        let ebadf = this.eval_libc("EBADF");
-        this.set_last_error(ebadf)?;
-        Ok((-1).into())
-    }
-
     fn file_type_to_d_type(
         &mut self,
         file_type: std::io::Result<FileType>,
@@ -726,6 +720,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         ))
     }
 
+    /// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
+    /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
+    /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
+    /// types (like `read`, that returns an `i64`).
+    fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
+        let this = self.eval_context_mut();
+        let ebadf = this.eval_libc("EBADF");
+        this.set_last_error(ebadf)?;
+        Ok((-1).into())
+    }
+
     fn read(
         &mut self,
         fd: i32,
diff --git a/src/tools/miri/src/shims/unix/linux/fd.rs b/src/tools/miri/src/shims/unix/linux/fd.rs
new file mode 100644
index 00000000000..212b7936341
--- /dev/null
+++ b/src/tools/miri/src/shims/unix/linux/fd.rs
@@ -0,0 +1,191 @@
+use rustc_middle::ty::ScalarInt;
+
+use crate::*;
+use epoll::{Epoll, EpollEvent};
+use event::Event;
+use socketpair::SocketPair;
+
+use shims::unix::fs::EvalContextExt as _;
+
+pub mod epoll;
+pub mod event;
+pub mod socketpair;
+
+impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
+pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
+    /// This function returns a file descriptor referring to the new `Epoll` instance. This file
+    /// descriptor is used for all subsequent calls to the epoll interface. If the `flags` argument
+    /// is 0, then this function is the same as `epoll_create()`.
+    ///
+    /// <https://linux.die.net/man/2/epoll_create1>
+    fn epoll_create1(
+        &mut self,
+        flags: &OpTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, Scalar<Provenance>> {
+        let this = self.eval_context_mut();
+
+        let flags = this.read_scalar(flags)?.to_i32()?;
+
+        let epoll_cloexec = this.eval_libc_i32("EPOLL_CLOEXEC");
+        if flags == epoll_cloexec {
+            // Miri does not support exec, so this flag has no effect.
+        } else if flags != 0 {
+            throw_unsup_format!("epoll_create1 flags {flags} are not implemented");
+        }
+
+        let fd = this.machine.file_handler.insert_fd(Box::new(Epoll::default()));
+        Ok(Scalar::from_i32(fd))
+    }
+
+    /// This function performs control operations on the `Epoll` instance referred to by the file
+    /// descriptor `epfd`. It requests that the operation `op` be performed for the target file
+    /// descriptor, `fd`.
+    ///
+    /// Valid values for the op argument are:
+    /// `EPOLL_CTL_ADD` - Register the target file descriptor `fd` on the `Epoll` instance referred
+    /// to by the file descriptor `epfd` and associate the event `event` with the internal file
+    /// linked to `fd`.
+    /// `EPOLL_CTL_MOD` - Change the event `event` associated with the target file descriptor `fd`.
+    /// `EPOLL_CTL_DEL` - Deregister the target file descriptor `fd` from the `Epoll` instance
+    /// referred to by `epfd`. The `event` is ignored and can be null.
+    ///
+    /// <https://linux.die.net/man/2/epoll_ctl>
+    fn epoll_ctl(
+        &mut self,
+        epfd: &OpTy<'tcx, Provenance>,
+        op: &OpTy<'tcx, Provenance>,
+        fd: &OpTy<'tcx, Provenance>,
+        event: &OpTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, Scalar<Provenance>> {
+        let this = self.eval_context_mut();
+
+        let epfd = this.read_scalar(epfd)?.to_i32()?;
+        let op = this.read_scalar(op)?.to_i32()?;
+        let fd = this.read_scalar(fd)?.to_i32()?;
+        let _event = this.read_scalar(event)?.to_pointer(this)?;
+
+        let epoll_ctl_add = this.eval_libc_i32("EPOLL_CTL_ADD");
+        let epoll_ctl_mod = this.eval_libc_i32("EPOLL_CTL_MOD");
+        let epoll_ctl_del = this.eval_libc_i32("EPOLL_CTL_DEL");
+
+        if op == epoll_ctl_add || op == epoll_ctl_mod {
+            let event = this.deref_operand(event)?;
+
+            let events = this.mplace_field(&event, 0)?;
+            let events = this.read_scalar(&events.into())?.to_u32()?;
+            let data = this.mplace_field(&event, 1)?;
+            let data = this.read_scalar(&data.into())?;
+            let event = EpollEvent { events, data };
+
+            if let Some(epfd) = this.machine.file_handler.handles.get_mut(&epfd) {
+                let epfd = epfd.as_epoll_handle()?;
+
+                epfd.file_descriptors.insert(fd, event);
+                Ok(Scalar::from_i32(0))
+            } else {
+                Ok(Scalar::from_i32(this.handle_not_found()?))
+            }
+        } else if op == epoll_ctl_del {
+            if let Some(epfd) = this.machine.file_handler.handles.get_mut(&epfd) {
+                let epfd = epfd.as_epoll_handle()?;
+
+                epfd.file_descriptors.remove(&fd);
+                Ok(Scalar::from_i32(0))
+            } else {
+                Ok(Scalar::from_i32(this.handle_not_found()?))
+            }
+        } else {
+            let einval = this.eval_libc("EINVAL");
+            this.set_last_error(einval)?;
+            Ok(Scalar::from_i32(-1))
+        }
+    }
+
+    /// This function creates an `Event` that is used as an event wait/notify mechanism by
+    /// user-space applications, and by the kernel to notify user-space applications of events.
+    /// The `Event` contains an `u64` counter maintained by the kernel. The counter is initialized
+    /// with the value specified in the `initval` argument.
+    ///
+    /// A new file descriptor referring to the `Event` is returned. The `read`, `write`, `poll`,
+    /// `select`, and `close` operations can be performed on the file descriptor. For more
+    /// information on these operations, see the man page linked below.
+    ///
+    /// The `flags` are not currently implemented for eventfd.
+    /// The `flags` may be bitwise ORed to change the behavior of `eventfd`:
+    /// `EFD_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor.
+    /// `EFD_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the new open file description.
+    /// `EFD_SEMAPHORE` - miri does not support semaphore-like semantics.
+    ///
+    /// <https://linux.die.net/man/2/eventfd>
+    fn eventfd(
+        &mut self,
+        val: &OpTy<'tcx, Provenance>,
+        flags: &OpTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, Scalar<Provenance>> {
+        let this = self.eval_context_mut();
+
+        let val = this.read_scalar(val)?.to_u32()?;
+        let flags = this.read_scalar(flags)?.to_i32()?;
+
+        let efd_cloexec = this.eval_libc_i32("EFD_CLOEXEC");
+        let efd_nonblock = this.eval_libc_i32("EFD_NONBLOCK");
+        let efd_semaphore = this.eval_libc_i32("EFD_SEMAPHORE");
+
+        if flags & (efd_cloexec | efd_nonblock | efd_semaphore) == 0 {
+            throw_unsup_format!("{flags} is unsupported");
+        }
+        // FIXME handle the cloexec and nonblock flags
+        if flags & efd_cloexec == efd_cloexec {}
+        if flags & efd_nonblock == efd_nonblock {}
+        if flags & efd_semaphore == efd_semaphore {
+            throw_unsup_format!("EFD_SEMAPHORE is unsupported");
+        }
+
+        let fh = &mut this.machine.file_handler;
+        let fd = fh.insert_fd(Box::new(Event { val }));
+        Ok(Scalar::from_i32(fd))
+    }
+
+    /// Currently this function creates new `SocketPair`s without specifying the domain, type, or
+    /// protocol of the new socket and these are stored in the socket values `sv` argument.
+    ///
+    /// This function creates an unnamed pair of connected sockets in the specified domain, of the
+    /// specified type, and using the optionally specified protocol.
+    ///
+    /// The `domain` argument specified a communication domain; this selects the protocol family
+    /// used for communication. The socket `type` specifies the communication semantics.
+    /// The `protocol` specifies a particular protocol to use with the socket. Normally there's
+    /// only a single protocol supported for a particular socket type within a given protocol
+    /// family, in which case `protocol` can be specified as 0. It is possible that many protocols
+    /// exist and in that case, a particular protocol must be specified.
+    ///
+    /// For more information on the arguments see the socket manpage:
+    /// <https://linux.die.net/man/2/socket>
+    ///
+    /// <https://linux.die.net/man/2/socketpair>
+    fn socketpair(
+        &mut self,
+        domain: &OpTy<'tcx, Provenance>,
+        type_: &OpTy<'tcx, Provenance>,
+        protocol: &OpTy<'tcx, Provenance>,
+        sv: &OpTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, Scalar<Provenance>> {
+        let this = self.eval_context_mut();
+
+        let _domain = this.read_scalar(domain)?.to_i32()?;
+        let _type_ = this.read_scalar(type_)?.to_i32()?;
+        let _protocol = this.read_scalar(protocol)?.to_i32()?;
+        let sv = this.deref_operand(sv)?;
+
+        let fh = &mut this.machine.file_handler;
+        let sv0 = fh.insert_fd(Box::new(SocketPair));
+        let sv0 = ScalarInt::try_from_int(sv0, sv.layout.size).unwrap();
+        let sv1 = fh.insert_fd(Box::new(SocketPair));
+        let sv1 = ScalarInt::try_from_int(sv1, sv.layout.size).unwrap();
+
+        this.write_scalar(sv0, &sv.into())?;
+        this.write_scalar(sv1, &sv.offset(sv.layout.size, sv.layout, this)?.into())?;
+
+        Ok(Scalar::from_i32(0))
+    }
+}
diff --git a/src/tools/miri/src/shims/unix/linux/fd/epoll.rs b/src/tools/miri/src/shims/unix/linux/fd/epoll.rs
new file mode 100644
index 00000000000..eb86773e6b6
--- /dev/null
+++ b/src/tools/miri/src/shims/unix/linux/fd/epoll.rs
@@ -0,0 +1,53 @@
+use crate::*;
+
+use crate::shims::unix::fs::FileDescriptor;
+
+use rustc_data_structures::fx::FxHashMap;
+use std::io;
+
+/// An `Epoll` file descriptor connects file handles and epoll events
+#[derive(Clone, Debug, Default)]
+pub struct Epoll {
+    /// The file descriptors we are watching, and what we are watching for.
+    pub file_descriptors: FxHashMap<i32, EpollEvent>,
+}
+
+/// Epoll Events associate events with data.
+/// These fields are currently unused by miri.
+/// This matches the `epoll_event` struct defined
+/// by the epoll_ctl man page. For more information
+/// see the man page:
+///
+/// <https://man7.org/linux/man-pages/man2/epoll_ctl.2.html>
+#[derive(Clone, Debug)]
+pub struct EpollEvent {
+    pub events: u32,
+    /// `Scalar<Provenance>` is used to represent the
+    /// `epoll_data` type union.
+    pub data: Scalar<Provenance>,
+}
+
+impl FileDescriptor for Epoll {
+    fn name(&self) -> &'static str {
+        "epoll"
+    }
+
+    fn as_epoll_handle<'tcx>(&mut self) -> InterpResult<'tcx, &mut Epoll> {
+        Ok(self)
+    }
+
+    fn dup<'tcx>(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
+        Ok(Box::new(self.clone()))
+    }
+
+    fn is_tty(&self) -> bool {
+        false
+    }
+
+    fn close<'tcx>(
+        self: Box<Self>,
+        _communicate_allowed: bool,
+    ) -> InterpResult<'tcx, io::Result<i32>> {
+        Ok(Ok(0))
+    }
+}
diff --git a/src/tools/miri/src/shims/unix/linux/fd/event.rs b/src/tools/miri/src/shims/unix/linux/fd/event.rs
new file mode 100644
index 00000000000..e87ff56bee3
--- /dev/null
+++ b/src/tools/miri/src/shims/unix/linux/fd/event.rs
@@ -0,0 +1,38 @@
+use crate::shims::unix::fs::FileDescriptor;
+
+use rustc_const_eval::interpret::InterpResult;
+
+use std::io;
+
+/// A kind of file descriptor created by `eventfd`.
+/// The `Event` type isn't currently written to by `eventfd`.
+/// The interface is meant to keep track of objects associated
+/// with a file descriptor. For more information see the man
+/// page below:
+///
+/// <https://man.netbsd.org/eventfd.2>
+#[derive(Debug)]
+pub struct Event {
+    pub val: u32,
+}
+
+impl FileDescriptor for Event {
+    fn name(&self) -> &'static str {
+        "event"
+    }
+
+    fn dup<'tcx>(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
+        Ok(Box::new(Event { val: self.val }))
+    }
+
+    fn is_tty(&self) -> bool {
+        false
+    }
+
+    fn close<'tcx>(
+        self: Box<Self>,
+        _communicate_allowed: bool,
+    ) -> InterpResult<'tcx, io::Result<i32>> {
+        Ok(Ok(0))
+    }
+}
diff --git a/src/tools/miri/src/shims/unix/linux/fd/socketpair.rs b/src/tools/miri/src/shims/unix/linux/fd/socketpair.rs
new file mode 100644
index 00000000000..036d3a2e31e
--- /dev/null
+++ b/src/tools/miri/src/shims/unix/linux/fd/socketpair.rs
@@ -0,0 +1,32 @@
+use crate::*;
+
+use crate::shims::unix::fs::FileDescriptor;
+
+use std::io;
+
+/// Pair of connected sockets.
+///
+/// We currently don't allow sending any data through this pair, so this can be just a dummy.
+#[derive(Debug)]
+pub struct SocketPair;
+
+impl FileDescriptor for SocketPair {
+    fn name(&self) -> &'static str {
+        "socketpair"
+    }
+
+    fn dup<'tcx>(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
+        Ok(Box::new(SocketPair))
+    }
+
+    fn is_tty(&self) -> bool {
+        false
+    }
+
+    fn close<'tcx>(
+        self: Box<Self>,
+        _communicate_allowed: bool,
+    ) -> InterpResult<'tcx, io::Result<i32>> {
+        Ok(Ok(0))
+    }
+}
diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
index ff7b2b352f2..be541deae40 100644
--- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
@@ -1,9 +1,11 @@
 use rustc_span::Symbol;
 use rustc_target::spec::abi::Abi;
 
+use crate::machine::SIGRTMAX;
 use crate::*;
 use shims::foreign_items::EmulateByNameResult;
 use shims::unix::fs::EvalContextExt as _;
+use shims::unix::linux::fd::EvalContextExt as _;
 use shims::unix::linux::sync::futex;
 use shims::unix::sync::EvalContextExt as _;
 use shims::unix::thread::EvalContextExt as _;
@@ -42,6 +44,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let result = this.sync_file_range(fd, offset, nbytes, flags)?;
                 this.write_scalar(result, dest)?;
             }
+            "epoll_create1" => {
+                let [flag] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let result = this.epoll_create1(flag)?;
+                this.write_scalar(result, dest)?;
+            }
+            "epoll_ctl" => {
+                let [epfd, op, fd, event] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let result = this.epoll_ctl(epfd, op, fd, event)?;
+                this.write_scalar(result, dest)?;
+            }
+            "eventfd" => {
+                let [val, flag] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let result = this.eventfd(val, flag)?;
+                this.write_scalar(result, dest)?;
+            }
+            "socketpair" => {
+                let [domain, type_, protocol, sv] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                let result = this.socketpair(domain, type_, protocol, sv)?;
+                this.write_scalar(result, dest)?;
+            }
+            "__libc_current_sigrtmax" => {
+                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+
+                this.write_scalar(Scalar::from_i32(SIGRTMAX), dest)?;
+            }
 
             // Threading
             "pthread_condattr_setclock" => {
diff --git a/src/tools/miri/src/shims/unix/linux/mod.rs b/src/tools/miri/src/shims/unix/linux/mod.rs
index 498eb57c57f..437764c3824 100644
--- a/src/tools/miri/src/shims/unix/linux/mod.rs
+++ b/src/tools/miri/src/shims/unix/linux/mod.rs
@@ -1,3 +1,4 @@
 pub mod dlsym;
+pub mod fd;
 pub mod foreign_items;
 pub mod sync;
diff --git a/src/tools/miri/tests/fail/crates/tokio_mvp.rs b/src/tools/miri/tests/fail/crates/tokio_mvp.rs
deleted file mode 100644
index 7cb42c09a96..00000000000
--- a/src/tools/miri/tests/fail/crates/tokio_mvp.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-//@compile-flags: -Zmiri-disable-isolation
-//@error-pattern: can't call foreign function: epoll_create1
-//@normalize-stderr-test: "   = note: inside .*\n" -> ""
-//@only-target-linux: the errors differ too much between platforms
-
-#[tokio::main]
-async fn main() {}
diff --git a/src/tools/miri/tests/fail/crates/tokio_mvp.stderr b/src/tools/miri/tests/fail/crates/tokio_mvp.stderr
deleted file mode 100644
index 1e7dfaa7499..00000000000
--- a/src/tools/miri/tests/fail/crates/tokio_mvp.stderr
+++ /dev/null
@@ -1,19 +0,0 @@
-error: unsupported operation: can't call foreign function: epoll_create1
-  --> CARGO_REGISTRY/.../epoll.rs:LL:CC
-   |
-LL |         let res = syscall!(epoll_create1(libc::EPOLL_CLOEXEC));
-   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't call foreign function: epoll_create1
-   |
-   = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
-   = note: BACKTRACE:
-note: inside `main`
-  --> $DIR/tokio_mvp.rs:LL:CC
-   |
-LL | #[tokio::main]
-   | ^^^^^^^^^^^^^^
-   = note: this error originates in the macro `syscall` which comes from the expansion of the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to previous error
-
diff --git a/src/tools/miri/tests/pass-dep/tokio_mvp.rs b/src/tools/miri/tests/pass-dep/tokio_mvp.rs
new file mode 100644
index 00000000000..642168253c2
--- /dev/null
+++ b/src/tools/miri/tests/pass-dep/tokio_mvp.rs
@@ -0,0 +1,6 @@
+// Need to disable preemption to stay on the supported MVP codepath in mio.
+//@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance -Zmiri-preemption-rate=0
+//@only-target-x86_64-unknown-linux: support for tokio exists only on linux and x86
+
+#[tokio::main]
+async fn main() {}