diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md
index 137e402b..e7c1fefa 100644
--- a/actix-service/CHANGES.md
+++ b/actix-service/CHANGES.md
@@ -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
diff --git a/actix-service/src/and_then.rs b/actix-service/src/and_then.rs
index 74b64b38..bf402e8f 100644
--- a/actix-service/src/and_then.rs
+++ b/actix-service/src/and_then.rs
@@ -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(Cell<(A, B)>);
+pub(crate) struct AndThenService(Rc>);
impl AndThenService {
/// Create new `AndThen` combinator
@@ -19,7 +20,7 @@ impl AndThenService {
A: Service,
B: Service,
{
- Self(Cell::new((a, b)))
+ Self(Rc::new(RefCell::new((a, b))))
}
}
@@ -40,7 +41,7 @@ where
type Future = AndThenServiceResponse;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> {
- 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
@@ -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())),
}
}
}
@@ -72,7 +73,7 @@ where
A: Service,
B: Service,
{
- A(#[pin] A::Future, Option>),
+ A(#[pin] A::Future, Option>>),
B(#[pin] B::Future),
Empty,
}
@@ -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)
}
diff --git a/actix-service/src/and_then_apply_fn.rs b/actix-service/src/and_then_apply_fn.rs
index de0cfac9..105d7ed7 100644
--- a/actix-service/src/and_then_apply_fn.rs
+++ b/actix-service/src/and_then_apply_fn.rs
@@ -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
@@ -16,7 +16,7 @@ where
Fut: Future |