-
Notifications
You must be signed in to change notification settings - Fork 4
Description
So, well, TimeCapsule<T> can accept a reference of just any lifetime for freeze. It produces a FrozenFuture<'a, 'b, T> that does have some lifetime corresponding to that reference, but all that ensures is that the reference stays alive until that future is polled, nothing else. If you do end up just .awaiting that future in-place (in particular, converting the Poll::pending of FrozenFuture into an immediately propagated Poll::pending of the surrounding future), then all may be well, but who forces you to do this?
That’s an easy exploit: Just poll the future, then invalidate the reference, and only then “propagate” the Poll::pending. Below is a straightforward implementation of this using helper macros from the futures crate family:
use std::marker::PhantomData;
use nolife::{BoxScope, Family, TimeCapsule};
use futures_util::{poll, pending};
struct SingleFamily<T: 'static>(PhantomData<T>);
impl<'a, T: 'static> Family<'a> for SingleFamily<T> {
type Family = T;
}
fn main() {
{
let mut scope = BoxScope::new(
|mut time_capsule: TimeCapsule<SingleFamily<String>>| async move {
let mut x = String::from("Hello World!");
let fut = time_capsule.freeze(&mut x);
let _ = poll!(fut); // puts reference into state
drop(x); // then invalidates reference
pending!(); // then yields
panic!();
},
);
scope.enter(|s| {
println!("{s}");
})
}
}outputs garbage data, e.g.:
b����
miri output
error: Undefined Behavior: out-of-bounds pointer use: alloc877 has been freed, so this pointer is dangling
--> /home/frank/.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/raw.rs:109:9
|
109 | &*ptr::slice_from_raw_parts(data, len)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: alloc877 has been freed, so this pointer is dangling
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
help: alloc877 was allocated here:
--> src/main.rs:14:29
|
14 | let mut x = String::from("Hello World!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: alloc877 was deallocated here:
--> src/main.rs:17:17
|
17 | drop(x); // then invalidates reference
| ^^^^^^^
= note: BACKTRACE (of the first span):
= note: inside `std::slice::from_raw_parts::<'_, u8>` at /home/frank/.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/raw.rs:109:9: 109:47
= note: inside `<std::vec::Vec<u8> as std::ops::Deref>::deref` at /home/frank/.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:2709:18: 2709:64
= note: inside `<std::string::String as std::ops::Deref>::deref` at /home/frank/.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/string.rs:2539:43: 2539:52
= note: inside `<std::string::String as std::fmt::Display>::fmt` at /home/frank/.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/string.rs:2354:28: 2354:34
= note: inside `<&mut std::string::String as std::fmt::Display>::fmt` at /home/frank/.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:2298:62: 2298:82
= note: inside `core::fmt::rt::Argument::<'_>::fmt` at /home/frank/.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/rt.rs:142:9: 142:40
= note: inside `std::fmt::write` at /home/frank/.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:1120:17: 1120:40
= note: inside `<std::io::StdoutLock<'_> as std::io::Write>::write_fmt` at /home/frank/.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/mod.rs:1846:15: 1846:43
= note: inside `<&std::io::Stdout as std::io::Write>::write_fmt` at /home/frank/.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/stdio.rs:736:9: 736:36
= note: inside `<std::io::Stdout as std::io::Write>::write_fmt` at /home/frank/.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/stdio.rs:710:9: 710:33
= note: inside `std::io::stdio::print_to::<std::io::Stdout>` at /home/frank/.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/stdio.rs:1029:21: 1029:47
= note: inside `std::io::_print` at /home/frank/.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/stdio.rs:1106:5: 1106:37
note: inside closure
--> src/main.rs:23:13
|
23 | println!("{s}");
| ^^^^^^^^^^^^^^^
= note: inside `nolife::scope::Scope::<SingleFamily<std::string::String>, {async block@src/main.rs:13:67: 20:14}>::enter::<'_, (), {closure@src/main.rs:22:21: 22:24}>` at /home/frank/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nolife-0.3.3/src/scope.rs:166:22: 166:30
= note: inside `nolife::BoxScope::<SingleFamily<std::string::String>, {async block@src/main.rs:13:67: 20:14}>::enter::<'_, (), {closure@src/main.rs:22:21: 22:24}>` at /home/frank/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nolife-0.3.3/src/box_scope.rs:117:18: 117:41
note: inside `main`
--> src/main.rs:22:9
|
22 | / scope.enter(|s| {
23 | | println!("{s}");
24 | | })
| |__________^
= note: this error originates in the macro `println` (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 1 previous error
Unfortunately, I cannot immediately think of straightforward fixes for soundness here.1 This might need more thought though. Let’s hope there is some alternative approach out there, and that his doesn’t mean this whole crate’s idea is fundamentally broken.
Footnotes
-
At least without an API that involves some mandatory usage of a macro that calls
unsafe, possibly-hidden, API. ↩