Skip to content

select/join allows for moving pinned futures #2

@udoprog

Description

@udoprog

Sorry to be the bearer of bad news. Pinning is a tricky subject and can be quite subtle.

Describe the bug

The current implementation of select allows for moving a future which is assumed to be pinned. Among other potential issues, this enables reading freed memory in safe code.

To Reproduce

The following should showcase the issue:

Edit: More compact example, and a bit more comments.

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::mem;
use futures::future;

use pasts::prelude::*;

async fn doit(leak: u128) {
    if leak == 42 {
        // Take a reference to leak which is a local variable to this function. And have it live across an await.
        let leak = &leak;
        println!("{} {:?}", leak, leak as *const _);

        let mut pending = true;

        // future that only returns pending the first time it's polled.
        future::poll_fn(move |_| if mem::take(&mut pending) {
            Poll::Pending
        } else {
            Poll::Ready(())
        }).await;

        println!("{} {:?}", leak, leak as *const _);
    } else {
        // do nothing to drive the select
    }
}

#[tokio::main]
async fn main() {
    let mut v = [doit(42), doit(0)];
    println!("old location of future: {:?}", v.as_ptr());

    v.select().await;

    // move the future.
    let a = std::mem::replace(&mut v[0], doit(0));
    println!("new location of future: {:?}", &a as *const _);
    a.await;
}

Running it in debug mode for me gives:

old location of future: 0x7ffe11d595b0
42 0x7ffe11d595c0
new location of future: 0x7ffe11d59618
2595995492391351414651693012353024 0x7ffe11d595c0

In effect: The second read of the reference to &foo uses an outdated memory location, since the future has been moved the second time it was polled.

Expected behavior

The select implementation should require the futures to be Unpin, or maintain Pin invariants in some other way to prevent this from compiling.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions