CVE-2024-27308 - Exploiting Use-After-Free in Mio Named Pipes on Windows (Rust)

On March 1, 2024, a critical vulnerability was disclosed in Mio, a popular I/O library for Rust used by projects like Tokio, the async runtime. This vulnerability, CVE-2024-27308, makes some Windows Rust applications vulnerable to use-after-free bugs when using named pipes—a dangerous scenario that could lead to crashes, unpredictable behavior, or even remote code execution under certain circumstances.

This post breaks down how the vulnerability works, why it matters, and how you can defend your Rust code. We'll keep the technical jargon to a minimum and include clear, exclusive code snippets. If you use Mio or Tokio on Windows, read carefully!

What is Mio?

Mio ("Metal IO") is a low-level I/O library for Rust. It provides the basic building blocks for asynchronous event loops by exposing OS-specific APIs (like epoll, kqueue, and IOCP). Popular async runtimes—including Tokio—use Mio under the hood.

The Problem: Deregistered Named Pipe Tokens

When you register a named pipe with Mio on Windows, Mio gives it a unique *token* (an identifier that helps the event loop track it). Later, you may deregister (remove) the pipe from Mio, which should make the token invalid.

Starting with Mio v.7.2 up to v.8.10, there's a bug: sometimes, Mio returns tokens for named pipes that have already been deregistered. Normally, this is just a logical inconsistency, but — depending on how you use tokens — it can be much worse.

Impact: Use-After-Free

For many applications, an invalid token may just be ignored, logged as a warning, or trigger a crash due to logic errors. But in some advanced patterns, the token is used to index into a data structure holding pointers to active objects.

Fetches a dangling pointer (because the original resource was freed),

You now have a classic use-after-free usage — a serious memory safety violation in Rust's usual guarantees.

> ⚠️ Tokio projects using named pipes are at particular risk:
> Tokio v1.30. and later, when used with a vulnerable version of Mio, will use the invalid tokens, which may result in memory unsafety (a use-after-free). Tokio versions earlier than 1.30. ignored these invalid tokens and are not vulnerable.

Code Walkthrough: Why This Happens

Here’s a minimal example to illustrate the danger. Do not run this in production.

use mio::{Events, Interest, Poll, Token};
use mio::windows::NamedPipe;
use std::sync::Arc;

struct StateHolder {
    // Assume you store pointers in a HashMap or Vec indexed by token
    pipes: Vec<Option<Arc<NamedPipe>>>,
}

fn main() -> std::io::Result<()> {
    let poll = Poll::new()?;
    let mut events = Events::with_capacity(128);
    let pipe = NamedPipe::new("\\\\.\\pipe\\mytestpipe")?;
    let token = Token();

    // Register the pipe with the poller
    poll.registry().register(&pipe, token, Interest::READABLE)?;

    // ... Later, you deregister (say, client disconnects)
    poll.registry().deregister(&pipe)?;

    // Old versions of mio may still return token in the event set!
    poll.poll(&mut events, None)?;

    for event in &events {
        // event.token() could be for the deregistered pipe!
        // If you use it as an index, it's a use-after-free risk!
        // E.g.: let pipe = state_holder.pipes[event.token().].as_ref().unwrap();
    }

    Ok(())
}

Real-world apps are much more complex, but the basic principle is the same: Mio can leak "zombie" tokens after deregistration, and if you assume they're always valid, you have a bug.

Crash: App panics or segfaults after receiving a "ghost" event.

- Use-After-Free: Your app stores references or pointers in a collection indexed by token. An attacker (or a race condition) may trigger access to freed memory.

For Tokio, this is serious: Modern Tokio (≥1.30.) forwards events as soon as Mio gives them, not performing extra token validation. A malicious actor capable of tricking the named pipe subsystem could, in theory, trigger predictable UAF scenarios.

All versions from v.7.2 through v.8.10 are vulnerable.

- Release notes

Short-term workaround:

If you cannot upgrade, you must check tokens and ignore unknown/deregistered ones.

if valid_token(event.token()) {

// Safe to use

} else {

// Skip, log warning

}

}

fn valid_token(token: Token) -> bool {

// Your own lookup logic, e.g., token < state_holder.pipes.len()
}

Audit your code:

Review where and how you use Mio tokens. Make sure you do not rely on their liveness without checking.

References

- Mio Advisory (GHSA-5f32-4fvx-qf3h)
- Tokio Twitter Announcement
- CVE record at MITRE
- Mio Issue #1696

In Summary

- CVE-2024-27308 is a serious, Windows-only, named pipes vulnerability in Mio (and by extension, Tokio and other libraries relying on Mio).

If you use named pipes with Mio on Windows, upgrade to Mio v.8.11 immediately.

- This bug can cause use-after-free if you index into data structures based on tokens without validating them.

Timeline

Published on: 03/06/2024 20:15:47 UTC
Last modified on: 03/06/2024 21:42:48 UTC