CVE-2024-27308 (Rust, published 2024-03-06T20:15Z)
Description: value: Mio is a Metal I/O library for Rust. When using named pipes on Windows, mio will under some circumstances return invalid tokens that correspond to named pipes that have already been deregistered from the mio registry. The impact of this vulnerability depends on how mio is used. For some applications, invalid tokens may be ignored or cause a warning or a crash. On the other hand, for applications that store pointers in the tokens, this vulnerability may result in a use-after-free. For users of Tokio, this vulnerability is serious and can result in a use-after-free in Tokio. The vulnerability is Windows-specific, and can only happen if you are using named pipes. Other IO resources are not affected. This vulnerability has been fixed in mio v0.8.11. All versions of mio between v0.7.2 and v0.8.10 are vulnerable. Tokio is vulnerable when you are using a vulnerable version of mio AND you are using at least Tokio v1.30.0. Versions of Tokio prior to v1.30.0 will ignore invalid tokens, so they are not vulnerable. Vulnerable libraries that use mio can work around this issue by detecting and ignoring invalid tokens.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/tokio-rs/mio
Files changed:
src/sys/windows/event.rs
src/sys/windows/named_pipe.rs
Commit message: named-pipes: fix receiving IOCP events after deregister

Backport of the following commits
 * 04c3bb6ef23d217ee7f8f68e3e6d0d9fcddd1520
 * dc3b3f63ba34e2189d9e57fd83b1d4ce5a52385e
 * 6fd5dd0d615a66a30a449d3f8eb1266dad96af62


Vulnerable code:
        // `read` is after `connect: Overlapped` and `read: Overlapped`.
                io.notify_readable(events);
                io.notify_writable(events);
                io.notify_writable(events);
    io.notify_readable(events);
            io.notify_writable(events);
                    io.notify_writable(events);
                io.notify_writable(events);
    fn notify_readable(&self, events: Option<&mut Vec<Event>>) {
                let _ = self.cp.as_ref().unwrap().post(ev.to_completion_status());
    fn notify_writable(&self, events: Option<&mut Vec<Event>>) {
                let _ = self.cp.as_ref().unwrap().post(ev.to_completion_status());

Fixed code:

    #[cfg(feature = "os-ext")]
    pub(super) fn to_completion_status_with_overlapped(
        &self,
        overlapped: *mut super::Overlapped,
    ) -> CompletionStatus {
        CompletionStatus::new(self.flags, self.data as usize, overlapped)
    }
    event: Overlapped,
        // `write` is after `connect: Overlapped` and `read: Overlapped`.
    /// Same as [`ptr_from_conn_overlapped`] but for `Inner.event`.
    unsafe fn ptr_from_event_overlapped(ptr: *mut OVERLAPPED) -> *const Inner {
        // `event` is after `connect: Overlapped`, `read: Overlapped`, and `write: Overlapped`.
        (ptr as *mut Overlapped).wrapping_sub(3) as *const Inner
    }

                event: Overlapped::new(event_done),
                io.notify_readable(me, events);
                io.notify_writable(me, events);
                io.notify_writable(me, events);
    io.notify_readable(&me, events);
            io.notify_writable(&me, events);
                    io.notify_writable(&me, events);
                io.notify_writable(&me, events);
fn event_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) {
    let status = CompletionStatus::from_entry(status);

    // Acquire the `Arc<Inner>`. Note that we should be guaranteed that
    // the refcount is available to us due to the `mem::forget` in
    // `schedule_write` above.
    let me = unsafe { Arc::from_raw(Inner::ptr_from_event_overlapped(status.overlapped())) };

    let io = me.io.lock().unwrap();

    // Make sure the I/O handle is still registered with the selector
    if io.token.is_some() {
        // This method is also called during `Selector::drop` to perform
        // cleanup. In this case, `events` is `None` and we don't need to track
        // the event.
        if let Some(events) = events {
            let mut ev = Event::from_completion_status(&status);
            // Reverse the `.data` alteration done in `schedule_event`. This
            // alteration was done so the selector recognized the event as one from
            // a named pipe.
            ev.data >>= 1;
            events.push(ev);
        }
    }
}

    fn notify_readable(&self, me: &Arc<Inner>, events: Option<&mut Vec<Event>>) {
                self.schedule_event(me, ev);
    fn notify_writable(&self, me: &Arc<Inner>, events: Option<&mut Vec<Event>>) {
                self.schedule_event(me, ev);
            }
        }
    }

    fn schedule_event(&self, me: &Arc<Inner>, mut event: Event) {
        // Alter the token so that the selector will identify the IOCP event as
        // one for a named pipe. This will be reversed in `event_done`
        //
        // `data` for named pipes is an auto-incrementing counter. Because
        // `data` is `u64` we do not risk losing the most-significant bit
        // (unless a user creates 2^62 named pipes during the lifetime of the
        // process).
        event.data <<= 1;
        event.data += 1;

        let completion_status =
            event.to_completion_status_with_overlapped(me.event.as_ptr() as *mut _);

        match self.cp.as_ref().unwrap().post(completion_status) {
            Ok(_) => {
                // Increase the ref count of `Inner` for the completion event.
                mem::forget(me.clone());
            }
            Err(_) => {
                // Nothing to do here