mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-24 00:21:08 +01:00
Remove several usages of 'unsafe' (#968)
* Replace UnsafeCell in DateServiceInner with Cell The previous API was extremely dangerous - calling `get_ref()` followed by `reset()` would trigger instant UB, without requiring any `unsafe` blocks in the caller. By making DateInner `Copy`, we can use a normal `Cell` instead of an `UnsafeCell`. This makes it impossible to cause UB (or even panic) with the API. * Split unsafe block HttpServiceHandlerResponse Also add explanation of the safety of the usage of `unsafe` * Replace UnsafeCell with RefCell in PayloadRef This ensures that a mistake in the usage of 'get_mut' will cause a panic, not undefined behavior.
This commit is contained in:
parent
2a2d7f5768
commit
b36fdc46db
@ -1,4 +1,4 @@
|
|||||||
use std::cell::UnsafeCell;
|
use std::cell::Cell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -172,6 +172,7 @@ impl ServiceConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
struct Date {
|
struct Date {
|
||||||
bytes: [u8; DATE_VALUE_LENGTH],
|
bytes: [u8; DATE_VALUE_LENGTH],
|
||||||
pos: usize,
|
pos: usize,
|
||||||
@ -205,28 +206,28 @@ impl fmt::Write for Date {
|
|||||||
struct DateService(Rc<DateServiceInner>);
|
struct DateService(Rc<DateServiceInner>);
|
||||||
|
|
||||||
struct DateServiceInner {
|
struct DateServiceInner {
|
||||||
current: UnsafeCell<Option<(Date, Instant)>>,
|
current: Cell<Option<(Date, Instant)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DateServiceInner {
|
impl DateServiceInner {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
DateServiceInner {
|
DateServiceInner {
|
||||||
current: UnsafeCell::new(None),
|
current: Cell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ref(&self) -> &Option<(Date, Instant)> {
|
fn get(&self) -> Option<(Date, Instant)> {
|
||||||
unsafe { &*self.current.get() }
|
self.current.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&self) {
|
fn reset(&self) {
|
||||||
unsafe { (&mut *self.current.get()).take() };
|
self.current.set(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self) {
|
fn update(&self) {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let date = Date::new();
|
let date = Date::new();
|
||||||
*(unsafe { &mut *self.current.get() }) = Some((date, now));
|
self.current.set(Some((date, now)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +237,7 @@ impl DateService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_date(&self) {
|
fn check_date(&self) {
|
||||||
if self.0.get_ref().is_none() {
|
if self.0.get().is_none() {
|
||||||
self.0.update();
|
self.0.update();
|
||||||
|
|
||||||
// periodic date update
|
// periodic date update
|
||||||
@ -252,14 +253,13 @@ impl DateService {
|
|||||||
|
|
||||||
fn now(&self) -> Instant {
|
fn now(&self) -> Instant {
|
||||||
self.check_date();
|
self.check_date();
|
||||||
self.0.get_ref().as_ref().unwrap().1
|
self.0.get().unwrap().1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn date(&self) -> &Date {
|
fn date(&self) -> Date {
|
||||||
self.check_date();
|
self.check_date();
|
||||||
|
|
||||||
let item = self.0.get_ref().as_ref().unwrap();
|
self.0.get().unwrap().0
|
||||||
&item.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,16 +466,18 @@ where
|
|||||||
State::Unknown(ref mut data) => {
|
State::Unknown(ref mut data) => {
|
||||||
if let Some(ref mut item) = data {
|
if let Some(ref mut item) = data {
|
||||||
loop {
|
loop {
|
||||||
unsafe {
|
// Safety - we only write to the returned slice.
|
||||||
let b = item.1.bytes_mut();
|
let b = unsafe { item.1.bytes_mut() };
|
||||||
let n = try_ready!(item.0.poll_read(b));
|
let n = try_ready!(item.0.poll_read(b));
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return Ok(Async::Ready(()));
|
return Ok(Async::Ready(()));
|
||||||
}
|
}
|
||||||
item.1.advance_mut(n);
|
// Safety - we know that 'n' bytes have
|
||||||
if item.1.len() >= HTTP2_PREFACE.len() {
|
// been initialized via the contract of
|
||||||
break;
|
// 'poll_read'
|
||||||
}
|
unsafe { item.1.advance_mut(n) };
|
||||||
|
if item.1.len() >= HTTP2_PREFACE.len() {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//! Multipart payload support
|
//! Multipart payload support
|
||||||
use std::cell::{Cell, RefCell, UnsafeCell};
|
use std::cell::{Cell, RefCell, RefMut};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::{cmp, fmt};
|
use std::{cmp, fmt};
|
||||||
@ -112,7 +112,7 @@ impl Stream for Multipart {
|
|||||||
Err(err)
|
Err(err)
|
||||||
} else if self.safety.current() {
|
} else if self.safety.current() {
|
||||||
let mut inner = self.inner.as_mut().unwrap().borrow_mut();
|
let mut inner = self.inner.as_mut().unwrap().borrow_mut();
|
||||||
if let Some(payload) = inner.payload.get_mut(&self.safety) {
|
if let Some(mut payload) = inner.payload.get_mut(&self.safety) {
|
||||||
payload.poll_stream()?;
|
payload.poll_stream()?;
|
||||||
}
|
}
|
||||||
inner.poll(&self.safety)
|
inner.poll(&self.safety)
|
||||||
@ -265,12 +265,12 @@ impl InnerMultipart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let headers = if let Some(payload) = self.payload.get_mut(safety) {
|
let headers = if let Some(mut payload) = self.payload.get_mut(safety) {
|
||||||
match self.state {
|
match self.state {
|
||||||
// read until first boundary
|
// read until first boundary
|
||||||
InnerState::FirstBoundary => {
|
InnerState::FirstBoundary => {
|
||||||
match InnerMultipart::skip_until_boundary(
|
match InnerMultipart::skip_until_boundary(
|
||||||
payload,
|
&mut *payload,
|
||||||
&self.boundary,
|
&self.boundary,
|
||||||
)? {
|
)? {
|
||||||
Some(eof) => {
|
Some(eof) => {
|
||||||
@ -286,7 +286,7 @@ impl InnerMultipart {
|
|||||||
}
|
}
|
||||||
// read boundary
|
// read boundary
|
||||||
InnerState::Boundary => {
|
InnerState::Boundary => {
|
||||||
match InnerMultipart::read_boundary(payload, &self.boundary)? {
|
match InnerMultipart::read_boundary(&mut *payload, &self.boundary)? {
|
||||||
None => return Ok(Async::NotReady),
|
None => return Ok(Async::NotReady),
|
||||||
Some(eof) => {
|
Some(eof) => {
|
||||||
if eof {
|
if eof {
|
||||||
@ -303,7 +303,7 @@ impl InnerMultipart {
|
|||||||
|
|
||||||
// read field headers for next field
|
// read field headers for next field
|
||||||
if self.state == InnerState::Headers {
|
if self.state == InnerState::Headers {
|
||||||
if let Some(headers) = InnerMultipart::read_headers(payload)? {
|
if let Some(headers) = InnerMultipart::read_headers(&mut *payload)? {
|
||||||
self.state = InnerState::Boundary;
|
self.state = InnerState::Boundary;
|
||||||
headers
|
headers
|
||||||
} else {
|
} else {
|
||||||
@ -411,7 +411,7 @@ impl Stream for Field {
|
|||||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||||
if self.safety.current() {
|
if self.safety.current() {
|
||||||
let mut inner = self.inner.borrow_mut();
|
let mut inner = self.inner.borrow_mut();
|
||||||
if let Some(payload) = inner.payload.as_ref().unwrap().get_mut(&self.safety)
|
if let Some(mut payload) = inner.payload.as_ref().unwrap().get_mut(&self.safety)
|
||||||
{
|
{
|
||||||
payload.poll_stream()?;
|
payload.poll_stream()?;
|
||||||
}
|
}
|
||||||
@ -582,12 +582,12 @@ impl InnerField {
|
|||||||
return Ok(Async::Ready(None));
|
return Ok(Async::Ready(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = if let Some(payload) = self.payload.as_ref().unwrap().get_mut(s) {
|
let result = if let Some(mut payload) = self.payload.as_ref().unwrap().get_mut(s) {
|
||||||
if !self.eof {
|
if !self.eof {
|
||||||
let res = if let Some(ref mut len) = self.length {
|
let res = if let Some(ref mut len) = self.length {
|
||||||
InnerField::read_len(payload, len)?
|
InnerField::read_len(&mut *payload, len)?
|
||||||
} else {
|
} else {
|
||||||
InnerField::read_stream(payload, &self.boundary)?
|
InnerField::read_stream(&mut *payload, &self.boundary)?
|
||||||
};
|
};
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
@ -618,7 +618,7 @@ impl InnerField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct PayloadRef {
|
struct PayloadRef {
|
||||||
payload: Rc<UnsafeCell<PayloadBuffer>>,
|
payload: Rc<RefCell<PayloadBuffer>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PayloadRef {
|
impl PayloadRef {
|
||||||
@ -628,15 +628,12 @@ impl PayloadRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_mut<'a, 'b>(&'a self, s: &'b Safety) -> Option<&'a mut PayloadBuffer>
|
fn get_mut<'a, 'b>(&'a self, s: &'b Safety) -> Option<RefMut<'a, PayloadBuffer>>
|
||||||
where
|
where
|
||||||
'a: 'b,
|
'a: 'b,
|
||||||
{
|
{
|
||||||
// Unsafe: Invariant is inforced by Safety Safety is used as ref counter,
|
|
||||||
// only top most ref can have mutable access to payload.
|
|
||||||
if s.current() {
|
if s.current() {
|
||||||
let payload: &mut PayloadBuffer = unsafe { &mut *self.payload.get() };
|
Some(self.payload.borrow_mut())
|
||||||
Some(payload)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user