Skip to content

Bug: nested closure outlives borrowed value. #53432

Closed
@alexander-irbis

Description

@alexander-irbis
extern crate futures;
extern crate tokio_core;

use futures::{future, Future};
use tokio_core::reactor::Core;

pub trait Action {
    type Output: Future<Item = (), Error = ()>;

    fn run(self) -> Self::Output;
}

impl<T: Future<Item=(), Error=()>, F: FnOnce() -> T> Action for F {
    type Output = T;

    fn run(self) -> Self::Output {
        self()
    }
}

fn retry<A: Action>(action: A) -> impl Future<Item = (), Error = ()> {
    action.run()
}

/*

The `lazy` closure avoids the check of its lifetime here, if:
- the `lazy` closure is nested into the `action` closure, and
- the `action` closure is passed into the `retry` function, and
- the `retry` function take a generic by the `Action` trait argument, and
- the `Action` trait is implemented for an `Fn*` trait.

As a result, we get arbitrary values in variables and at best SIGSEGV.

*/

fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    for i in &[1, 2, 3, 4, 5] {
        println!("outer: {}", i);

        let f = move || {
            println!("inner: {}", i);
            future::ok::<(), ()>(())
        };

        let action = move || {
            future::lazy(|| { // The `lazy` closure
                f()
            })
        };
        handle.spawn(retry(action))
    }

    core.run(future::empty::<(), ()>()).expect("Core::run");
}

(Playground)

Output:

outer: 1
outer: 2
outer: 3
outer: 4
outer: 5
inner: 1027752016
inner: 1027752016
inner: 1027752016
inner: 1027752016
inner: 1027752016

Errors:

   Compiling playground v0.0.1 (file:///playground)
    Finished dev [unoptimized + debuginfo] target(s) in 2.29s
     Running `target/debug/playground`
/root/entrypoint.sh: line 8:     8 Killed                  timeout --signal=KILL ${timeout} "$@"

This emits a warning in the 2018 edition mode, but silently accepts code leading to UB in the 2015 edition.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-NLLArea: Non-lexical lifetimes (NLL)A-borrow-checkerArea: The borrow checkerA-closuresArea: Closures (`|…| { … }`)C-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundnessfixed-by-NLLBugs fixed, but only when NLL is enabled.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions