diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md
index 137e402bd8..e7c1fefa80 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 74b64b3892..bf402e8f01 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 de0cfac9c7..105d7ed77c 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 |