Skip to content

Commit

Permalink
Remove unsound custom Cell (#158)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shnatsel authored Jul 19, 2020
1 parent 334c985 commit a67e38b
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 98 deletions.
6 changes: 6 additions & 0 deletions actix-service/CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changes

## Unreleased

### Fixed

* Removed unsound custom Cell implementation that allowed obtaining several mutable references to the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through service combinators. Attempts to acquire several mutable references to the same data will instead result in a panic.

## [1.0.5] - 2020-01-16

### Fixed
Expand Down
17 changes: 9 additions & 8 deletions actix-service/src/and_then.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::cell::RefCell;
use std::task::{Context, Poll};

use super::{Service, ServiceFactory};
use crate::cell::Cell;


/// Service for the `and_then` combinator, chaining a computation onto the end
/// of another service which completes successfully.
///
/// This is created by the `ServiceExt::and_then` method.
pub(crate) struct AndThenService<A, B>(Cell<(A, B)>);
pub(crate) struct AndThenService<A, B>(Rc<RefCell<(A, B)>>);

impl<A, B> AndThenService<A, B> {
/// Create new `AndThen` combinator
Expand All @@ -19,7 +20,7 @@ impl<A, B> AndThenService<A, B> {
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
Self(Cell::new((a, b)))
Self(Rc::new(RefCell::new((a, b))))
}
}

Expand All @@ -40,7 +41,7 @@ where
type Future = AndThenServiceResponse<A, B>;

fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let srv = self.0.get_mut();
let mut srv = self.0.borrow_mut();
let not_ready = !srv.0.poll_ready(cx)?.is_ready();
if !srv.1.poll_ready(cx)?.is_ready() || not_ready {
Poll::Pending
Expand All @@ -51,7 +52,7 @@ where

fn call(&mut self, req: A::Request) -> Self::Future {
AndThenServiceResponse {
state: State::A(self.0.get_mut().0.call(req), Some(self.0.clone())),
state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())),
}
}
}
Expand All @@ -72,7 +73,7 @@ where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
A(#[pin] A::Future, Option<Cell<(A, B)>>),
A(#[pin] A::Future, Option<Rc<RefCell<(A, B)>>>),
B(#[pin] B::Future),
Empty,
}
Expand All @@ -90,9 +91,9 @@ where
match this.state.as_mut().project() {
StateProj::A(fut, b) => match fut.poll(cx)? {
Poll::Ready(res) => {
let mut b = b.take().unwrap();
let b = b.take().unwrap();
this.state.set(State::Empty); // drop fut A
let fut = b.get_mut().1.call(res);
let fut = b.borrow_mut().1.call(res);
this.state.set(State::B(fut));
self.poll(cx)
}
Expand Down
22 changes: 11 additions & 11 deletions actix-service/src/and_then_apply_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::Rc;
use std::cell::RefCell;
use std::task::{Context, Poll};

use crate::cell::Cell;
use crate::{Service, ServiceFactory};

/// `Apply` service combinator
Expand All @@ -16,7 +16,7 @@ where
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
srv: Cell<(A, B, F)>,
srv: Rc<RefCell<(A, B, F)>>,
r: PhantomData<(Fut, Res, Err)>,
}

Expand All @@ -31,7 +31,7 @@ where
/// Create new `Apply` combinator
pub(crate) fn new(a: A, b: B, f: F) -> Self {
Self {
srv: Cell::new((a, b, f)),
srv: Rc::new(RefCell::new((a, b, f))),
r: PhantomData,
}
}
Expand Down Expand Up @@ -67,7 +67,7 @@ where
type Future = AndThenApplyFnFuture<A, B, F, Fut, Res, Err>;

fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let inner = self.srv.get_mut();
let mut inner = self.srv.borrow_mut();
let not_ready = inner.0.poll_ready(cx)?.is_pending();
if inner.1.poll_ready(cx)?.is_pending() || not_ready {
Poll::Pending
Expand All @@ -77,7 +77,7 @@ where
}

fn call(&mut self, req: A::Request) -> Self::Future {
let fut = self.srv.get_mut().0.call(req);
let fut = self.srv.borrow_mut().0.call(req);
AndThenApplyFnFuture {
state: State::A(fut, Some(self.srv.clone())),
}
Expand Down Expand Up @@ -108,7 +108,7 @@ where
Err: From<A::Error>,
Err: From<B::Error>,
{
A(#[pin] A::Future, Option<Cell<(A, B, F)>>),
A(#[pin] A::Future, Option<Rc<RefCell<(A, B, F)>>>),
B(#[pin] Fut),
Empty,
}
Expand All @@ -129,10 +129,10 @@ where
match this.state.as_mut().project() {
StateProj::A(fut, b) => match fut.poll(cx)? {
Poll::Ready(res) => {
let mut b = b.take().unwrap();
let b = b.take().unwrap();
this.state.set(State::Empty);
let b = b.get_mut();
let fut = (&mut b.2)(res, &mut b.1);
let (_, b, f) = &mut *b.borrow_mut();
let fut = f(res, b);
this.state.set(State::B(fut));
self.poll(cx)
}
Expand Down Expand Up @@ -255,11 +255,11 @@ where

if this.a.is_some() && this.b.is_some() {
Poll::Ready(Ok(AndThenApplyFn {
srv: Cell::new((
srv: Rc::new(RefCell::new((
this.a.take().unwrap(),
this.b.take().unwrap(),
this.f.clone(),
)),
))),
r: PhantomData,
}))
} else {
Expand Down
28 changes: 15 additions & 13 deletions actix-service/src/apply_cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::rc::Rc;
use std::cell::RefCell;

use crate::cell::Cell;
use crate::{Service, ServiceFactory};

/// Convert `Fn(Config, &mut Service1) -> Future<Service2>` fn to a service factory
Expand All @@ -26,7 +27,7 @@ where
S: Service,
{
ApplyConfigService {
srv: Cell::new((srv, f)),
srv: Rc::new(RefCell::new((srv, f))),
_t: PhantomData,
}
}
Expand All @@ -53,7 +54,7 @@ where
S: Service,
{
ApplyConfigServiceFactory {
srv: Cell::new((factory, f)),
srv: Rc::new(RefCell::new((factory, f))),
_t: PhantomData,
}
}
Expand All @@ -66,7 +67,7 @@ where
R: Future<Output = Result<S, E>>,
S: Service,
{
srv: Cell<(T, F)>,
srv: Rc<RefCell<(T, F)>>,
_t: PhantomData<(C, R, S)>,
}

Expand Down Expand Up @@ -102,10 +103,8 @@ where
type Future = R;

fn new_service(&self, cfg: C) -> Self::Future {
unsafe {
let srv = self.srv.get_mut_unsafe();
(srv.1)(cfg, &mut srv.0)
}
let (t, f) = &mut *self.srv.borrow_mut();
f(cfg, t)
}
}

Expand All @@ -117,7 +116,7 @@ where
R: Future<Output = Result<S, T::InitError>>,
S: Service,
{
srv: Cell<(T, F)>,
srv: Rc<RefCell<(T, F)>>,
_t: PhantomData<(C, R, S)>,
}

Expand Down Expand Up @@ -157,7 +156,7 @@ where
ApplyConfigServiceFactoryResponse {
cfg: Some(cfg),
store: self.srv.clone(),
state: State::A(self.srv.get_ref().0.new_service(())),
state: State::A(self.srv.borrow().0.new_service(())),
}
}
}
Expand All @@ -172,7 +171,7 @@ where
S: Service,
{
cfg: Option<C>,
store: Cell<(T, F)>,
store: Rc<RefCell<(T, F)>>,
#[pin]
state: State<T, R, S>,
}
Expand Down Expand Up @@ -213,8 +212,11 @@ where
},
StateProj::B(srv) => match srv.poll_ready(cx)? {
Poll::Ready(_) => {
let fut = (this.store.get_mut().1)(this.cfg.take().unwrap(), srv);
this.state.set(State::C(fut));
{
let (_, f) = &mut *this.store.borrow_mut();
let fut = f(this.cfg.take().unwrap(), srv);
this.state.set(State::C(fut));
}
self.poll(cx)
}
Poll::Pending => Poll::Pending,
Expand Down
57 changes: 0 additions & 57 deletions actix-service/src/cell.rs

This file was deleted.

1 change: 0 additions & 1 deletion actix-service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ mod and_then_apply_fn;
mod apply;
mod apply_cfg;
pub mod boxed;
mod cell;
mod fn_service;
mod map;
mod map_config;
Expand Down
16 changes: 8 additions & 8 deletions actix-service/src/then.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::cell::RefCell;
use std::task::{Context, Poll};

use super::{Service, ServiceFactory};
use crate::cell::Cell;

/// Service for the `then` combinator, chaining a computation onto the end of
/// another service.
///
/// This is created by the `Pipeline::then` method.
pub(crate) struct ThenService<A, B>(Cell<(A, B)>);
pub(crate) struct ThenService<A, B>(Rc<RefCell<(A, B)>>);

impl<A, B> ThenService<A, B> {
/// Create new `.then()` combinator
Expand All @@ -19,7 +19,7 @@ impl<A, B> ThenService<A, B> {
A: Service,
B: Service<Request = Result<A::Response, A::Error>, Error = A::Error>,
{
Self(Cell::new((a, b)))
Self(Rc::new(RefCell::new((a, b))))
}
}

Expand All @@ -40,7 +40,7 @@ where
type Future = ThenServiceResponse<A, B>;

fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let srv = self.0.get_mut();
let mut srv = self.0.borrow_mut();
let not_ready = !srv.0.poll_ready(cx)?.is_ready();
if !srv.1.poll_ready(cx)?.is_ready() || not_ready {
Poll::Pending
Expand All @@ -51,7 +51,7 @@ where

fn call(&mut self, req: A::Request) -> Self::Future {
ThenServiceResponse {
state: State::A(self.0.get_mut().0.call(req), Some(self.0.clone())),
state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())),
}
}
}
Expand All @@ -72,7 +72,7 @@ where
A: Service,
B: Service<Request = Result<A::Response, A::Error>>,
{
A(#[pin] A::Future, Option<Cell<(A, B)>>),
A(#[pin] A::Future, Option<Rc<RefCell<(A, B)>>>),
B(#[pin] B::Future),
Empty,
}
Expand All @@ -90,9 +90,9 @@ where
match this.state.as_mut().project() {
StateProj::A(fut, b) => match fut.poll(cx) {
Poll::Ready(res) => {
let mut b = b.take().unwrap();
let b = b.take().unwrap();
this.state.set(State::Empty); // drop fut A
let fut = b.get_mut().1.call(res);
let fut = b.borrow_mut().1.call(res);
this.state.set(State::B(fut));
self.poll(cx)
}
Expand Down

0 comments on commit a67e38b

Please sign in to comment.