1
0
mirror of https://github.com/fafhrd91/actix-net synced 2024-11-27 16:52:58 +01:00

address soundness footgun in poll_fn

This commit is contained in:
Rob Ede 2022-10-21 03:14:38 +01:00
parent cb83922b29
commit ade71b7bd3
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
3 changed files with 63 additions and 8 deletions

View File

@ -1,4 +1,4 @@
//! Asynchronous values.
//! Helpers for constructing futures.
mod either;
mod poll_fn;

View File

@ -8,6 +8,31 @@ use core::{
};
/// Creates a future driven by the provided function that receives a task context.
///
/// # Examples
/// ```
/// # use std::task::Poll;
/// # use actix_utils::future::poll_fn;
/// # async fn test_poll_fn() {
/// let res = poll_fn(|_| Poll::Ready(42)).await;
/// assert_eq!(res, 42);
///
/// let mut i = 5;
/// let res = poll_fn(|cx| {
/// i -= 1;
///
/// if i > 0 {
/// cx.waker().wake_by_ref();
/// Poll::Pending
/// } else {
/// Poll::Ready(42)
/// }
/// })
/// .await;
/// assert_eq!(res, 42);
/// # }
/// # actix_rt::Runtime::new().unwrap().block_on(test_poll_fn());
/// ```
#[inline]
pub fn poll_fn<F, T>(f: F) -> PollFn<F>
where
@ -16,13 +41,11 @@ where
PollFn { f }
}
/// A Future driven by the inner function.
/// Future for the [`poll_fn`] function.
pub struct PollFn<F> {
f: F,
}
impl<F> Unpin for PollFn<F> {}
impl<F> fmt::Debug for PollFn<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PollFn").finish()
@ -36,15 +59,22 @@ where
type Output = T;
#[inline]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
(self.f)(cx)
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// SAFETY: we are not moving out of the pinned field
// see https://github.com/rust-lang/rust/pull/102737
(unsafe { &mut self.get_unchecked_mut().f })(cx)
}
}
#[cfg(test)]
mod tests {
use std::marker::PhantomPinned;
use super::*;
static_assertions::assert_impl_all!(PollFn<()>: Unpin);
static_assertions::assert_not_impl_all!(PollFn<PhantomPinned>: Unpin);
#[actix_rt::test]
async fn test_poll_fn() {
let res = poll_fn(|_| Poll::Ready(42)).await;
@ -64,4 +94,29 @@ mod tests {
.await;
assert_eq!(res, 42);
}
// following soundness tests taken from https://github.com/tokio-rs/tokio/pull/5087
#[allow(dead_code)]
fn require_send<T: Send>(_t: &T) {}
#[allow(dead_code)]
fn require_sync<T: Sync>(_t: &T) {}
trait AmbiguousIfUnpin<A> {
fn some_item(&self) {}
}
impl<T: ?Sized> AmbiguousIfUnpin<()> for T {}
impl<T: ?Sized + Unpin> AmbiguousIfUnpin<[u8; 0]> for T {}
const _: fn() = || {
let pinned = std::marker::PhantomPinned;
let f = poll_fn(move |_| {
// Use `pinned` to take ownership of it.
let _ = &pinned;
std::task::Poll::Pending::<()>
});
require_send(&f);
require_sync(&f);
AmbiguousIfUnpin::some_item(&f);
};
}

View File

@ -1,4 +1,4 @@
//! When MSRV is 1.48, replace with `core::future::Ready` and `core::future::ready()`.
//! When `core::future::Ready` has a `into_inner()` method, this can be deprecated.
use core::{
future::Future,
@ -6,7 +6,7 @@ use core::{
task::{Context, Poll},
};
/// Future for the [`ready`](ready()) function.
/// Future for the [`ready`] function.
///
/// Panic will occur if polled more than once.
///