1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-06-25 14:49:20 +02:00

Use Pin<Box<S>> in BodyStream and SizedStream (#1328)

Fixes #1321

A better fix would be to change `MessageBody` to take a `Pin<&mut
Self>`, rather than a `Pin<&mut Self>`. This will avoid requiring the
use of `Box` for all consumers by allowing the caller to determine how
to pin the `MessageBody` implementation (e.g. via stack pinning).

However, doing so is a breaking change that will affect every user of
`MessageBody`. By pinning the inner stream ourselves, we can fix the
undefined behavior without breaking the API.

I've included @sebzim4500's reproduction case as a new test case.
However, due to the nature of undefined behavior, this could pass (and
not segfault) even if underlying issue were to regress.

Unfortunately, until rust-lang/unsafe-code-guidelines#148 is resolved,
it's not even possible to write a Miri test that will pass when the bug
is fixed.

Co-authored-by: Yuki Okushi <huyuumi.dev@gmail.com>
This commit is contained in:
Aaron Hill
2020-01-30 19:39:34 -05:00
committed by GitHub
parent 3033f187d2
commit fe13789345
2 changed files with 32 additions and 10 deletions

View File

@ -361,10 +361,8 @@ impl MessageBody for String {
/// Type represent streaming body.
/// Response does not contain `content-length` header and appropriate transfer encoding is used.
#[pin_project]
pub struct BodyStream<S, E> {
#[pin]
stream: S,
stream: Pin<Box<S>>,
_t: PhantomData<E>,
}
@ -375,7 +373,7 @@ where
{
pub fn new(stream: S) -> Self {
BodyStream {
stream,
stream: Box::pin(stream),
_t: PhantomData,
}
}
@ -396,7 +394,7 @@ where
/// ended on a zero-length chunk, but rather proceed until the underlying
/// [`Stream`] ends.
fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Bytes, Error>>> {
let mut stream = unsafe { Pin::new_unchecked(self) }.project().stream;
let mut stream = self.stream.as_mut();
loop {
return Poll::Ready(match ready!(stream.as_mut().poll_next(cx)) {
Some(Ok(ref bytes)) if bytes.is_empty() => continue,
@ -408,11 +406,9 @@ where
/// Type represent streaming body. This body implementation should be used
/// if total size of stream is known. Data get sent as is without using transfer encoding.
#[pin_project]
pub struct SizedStream<S> {
size: u64,
#[pin]
stream: S,
stream: Pin<Box<S>>,
}
impl<S> SizedStream<S>
@ -420,7 +416,7 @@ where
S: Stream<Item = Result<Bytes, Error>>,
{
pub fn new(size: u64, stream: S) -> Self {
SizedStream { size, stream }
SizedStream { size, stream: Box::pin(stream) }
}
}
@ -438,7 +434,7 @@ where
/// ended on a zero-length chunk, but rather proceed until the underlying
/// [`Stream`] ends.
fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Bytes, Error>>> {
let mut stream = unsafe { Pin::new_unchecked(self) }.project().stream;
let mut stream = self.stream.as_mut();
loop {
return Poll::Ready(match ready!(stream.as_mut().poll_next(cx)) {
Some(Ok(ref bytes)) if bytes.is_empty() => continue,