Skip to content

Commit 97d1d3e

Browse files
authored
Fix unsoundness issues #106 and #107 (#109)
1 parent 0588ec1 commit 97d1d3e

File tree

9 files changed

+127
-12
lines changed

9 files changed

+127
-12
lines changed

async-stream-impl/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "async-stream-impl"
33
version = "0.3.5"
44
edition = "2021"
5-
rust-version = "1.56"
5+
rust-version = "1.65"
66
license = "MIT"
77
authors = ["Carl Lerche <[email protected]>"]
88
description = "proc macros for async-stream crate"

async-stream-impl/src/lib.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,19 @@ impl VisitMut for Scrub<'_> {
120120

121121
// let ident = &self.yielder;
122122

123-
*i = if self.is_try {
124-
syn::parse_quote! { __yield_tx.send(::core::result::Result::Ok(#value_expr)).await }
123+
let yield_expr = if self.is_try {
124+
quote! { __yield_tx.send(::core::result::Result::Ok(#value_expr)).await }
125125
} else {
126-
syn::parse_quote! { __yield_tx.send(#value_expr).await }
126+
quote! { __yield_tx.send(#value_expr).await }
127+
};
128+
*i = syn::parse_quote! {
129+
{
130+
#[allow(unreachable_code)]
131+
if false {
132+
break '__async_stream_private_check_scope (loop {});
133+
}
134+
#yield_expr
135+
}
127136
};
128137
}
129138
syn::Expr::Try(try_expr) => {
@@ -225,8 +234,10 @@ pub fn stream_inner(input: TokenStream) -> TokenStream {
225234
quote!({
226235
let (mut __yield_tx, __yield_rx) = unsafe { #crate_path::__private::yielder::pair() };
227236
#crate_path::__private::AsyncStream::new(__yield_rx, async move {
228-
#dummy_yield
229-
#(#stmts)*
237+
'__async_stream_private_check_scope: {
238+
#dummy_yield
239+
#(#stmts)*
240+
}
230241
})
231242
})
232243
.into()
@@ -259,8 +270,10 @@ pub fn try_stream_inner(input: TokenStream) -> TokenStream {
259270
quote!({
260271
let (mut __yield_tx, __yield_rx) = unsafe { #crate_path::__private::yielder::pair() };
261272
#crate_path::__private::AsyncStream::new(__yield_rx, async move {
262-
#dummy_yield
263-
#(#stmts)*
273+
'__async_stream_private_check_scope: {
274+
#dummy_yield
275+
#(#stmts)*
276+
}
264277
})
265278
})
266279
.into()

async-stream/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ name = "async-stream"
55
# - Create git tag
66
version = "0.3.5"
77
edition = "2021"
8-
rust-version = "1.56"
8+
rust-version = "1.65"
99
license = "MIT"
1010
authors = ["Carl Lerche <[email protected]>"]
1111
description = "Asynchronous streams using async & await notation"

async-stream/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ caller.
152152

153153
## Supported Rust Versions
154154

155-
The current minimum supported Rust version is 1.56.
155+
The current minimum supported Rust version is 1.65.
156156

157157
## License
158158

async-stream/src/yielder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::task::{Context, Poll};
77

88
#[derive(Debug)]
99
pub struct Sender<T> {
10-
_p: PhantomData<T>,
10+
_p: PhantomData<fn(T) -> T>,
1111
}
1212

1313
#[derive(Debug)]
@@ -34,7 +34,7 @@ pub unsafe fn pair<T>() -> (Sender<T>, Receiver<T>) {
3434
// Tracks the pointer to `Option<T>`.
3535
//
3636
// TODO: Ensure wakers match?
37-
thread_local!(static STORE: Cell<*mut ()> = Cell::new(ptr::null_mut()));
37+
thread_local!(static STORE: Cell<*mut ()> = const { Cell::new(ptr::null_mut()) });
3838

3939
// ===== impl Sender =====
4040

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use async_stream::stream;
2+
use futures_util::StreamExt;
3+
4+
use std::pin::pin;
5+
6+
macro_rules! asynk {
7+
($e:expr) => {
8+
async { $e }
9+
};
10+
}
11+
12+
#[tokio::main]
13+
async fn main() {
14+
pin!(stream! {
15+
let yield_42 = asynk!(yield 42_usize);
16+
let s = stream! {
17+
yield Box::new(12345);
18+
yield_42.await; // yield 42 -- wait that's not a Box!?
19+
};
20+
for await (n, i) in s.enumerate() {
21+
println!("Item at index {n}:\n {i}");
22+
// Item at index 0:
23+
// 12345
24+
// Item at index 1:
25+
// Segmentation fault
26+
}
27+
})
28+
.next()
29+
.await;
30+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error[E0767]: use of unreachable label `'__async_stream_private_check_scope`
2+
--> tests/ui/unsoundness_issue_106.rs:14:10
3+
|
4+
14 | pin!(stream! {
5+
| __________^
6+
15 | | let yield_42 = asynk!(yield 42_usize);
7+
16 | | let s = stream! {
8+
17 | | yield Box::new(12345);
9+
... |
10+
26 | | }
11+
27 | | })
12+
| | ^
13+
| | |
14+
| |_____unreachable label `'__async_stream_private_check_scope`
15+
| unreachable label defined here
16+
|
17+
= note: labels are unreachable through functions, closures, async blocks and modules
18+
= note: this error originates in the macro `$crate::__private::stream_inner` which comes from the expansion of the macro `stream` (in Nightly builds, run with -Z macro-backtrace for more info)
19+
20+
error[E0267]: `break` inside `async` block
21+
--> tests/ui/unsoundness_issue_106.rs:14:10
22+
|
23+
8 | async { $e }
24+
| ----- enclosing `async` block
25+
...
26+
14 | pin!(stream! {
27+
| __________^
28+
15 | | let yield_42 = asynk!(yield 42_usize);
29+
16 | | let s = stream! {
30+
17 | | yield Box::new(12345);
31+
... |
32+
26 | | }
33+
27 | | })
34+
| |_____^ cannot `break` inside `async` block
35+
|
36+
= note: this error originates in the macro `$crate::__private::stream_inner` which comes from the expansion of the macro `stream` (in Nightly builds, run with -Z macro-backtrace for more info)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use async_stream::stream;
2+
use futures_util::StreamExt;
3+
4+
use std::pin::pin;
5+
6+
#[tokio::main]
7+
async fn main() {
8+
let mut outer = vec![];
9+
{
10+
let v = vec![0; 10];
11+
let v_ref = &v;
12+
let mut s = pin!(stream! {
13+
for x in v_ref {
14+
yield x
15+
}
16+
});
17+
while let Some(x) = s.next().await {
18+
outer.push(x);
19+
}
20+
};
21+
// use-after-free
22+
println!("{outer:?}"); // […garbage allocator internals…, 0, 0, 0]
23+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0597]: `v` does not live long enough
2+
--> tests/ui/unsoundness_issue_107.rs:11:21
3+
|
4+
10 | let v = vec![0; 10];
5+
| - binding `v` declared here
6+
11 | let v_ref = &v;
7+
| ^^ borrowed value does not live long enough
8+
...
9+
20 | };
10+
| - `v` dropped here while still borrowed
11+
21 | // use-after-free
12+
22 | println!("{outer:?}"); // […garbage allocator internals…, 0, 0, 0]
13+
| --------- borrow later used here

0 commit comments

Comments
 (0)