1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2025-01-08 16:25:29 +00:00

InternalError can trigger memory unsafety #301

This commit is contained in:
Nikolay Kim 2018-06-11 18:52:54 -07:00
parent 75ed053a35
commit 7d39f1582e
2 changed files with 46 additions and 31 deletions

View file

@ -1,8 +1,8 @@
//! Error and Result module //! Error and Result module
use std::cell::RefCell;
use std::io::Error as IoError; use std::io::Error as IoError;
use std::str::Utf8Error; use std::str::Utf8Error;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use std::sync::Mutex;
use std::{fmt, io, result}; use std::{fmt, io, result};
use actix::MailboxError; use actix::MailboxError;
@ -23,7 +23,7 @@ pub use cookie::ParseError as CookieParseError;
use handler::Responder; use handler::Responder;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::{HttpResponse, InnerHttpResponse};
/// A specialized [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html) /// A specialized [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html)
/// for actix web operations /// for actix web operations
@ -50,7 +50,9 @@ pub struct Error {
impl Error { impl Error {
/// Deprecated way to reference the underlying response error. /// Deprecated way to reference the underlying response error.
#[deprecated(since = "0.6.0", note = "please use `Error::as_response_error()` instead")] #[deprecated(
since = "0.6.0", note = "please use `Error::as_response_error()` instead"
)]
pub fn cause(&self) -> &ResponseError { pub fn cause(&self) -> &ResponseError {
self.cause.as_ref() self.cause.as_ref()
} }
@ -77,7 +79,8 @@ impl Error {
} }
} }
/// Attempts to downcast this `Error` to a particular `Fail` type by reference. /// Attempts to downcast this `Error` to a particular `Fail` type by
/// reference.
/// ///
/// If the underlying error is not of type `T`, this will return `None`. /// If the underlying error is not of type `T`, this will return `None`.
pub fn downcast_ref<T: Fail>(&self) -> Option<&T> { pub fn downcast_ref<T: Fail>(&self) -> Option<&T> {
@ -96,14 +99,13 @@ impl Error {
// //
// This currently requires a transmute. This could be avoided if failure // This currently requires a transmute. This could be avoided if failure
// provides a deref: https://github.com/rust-lang-nursery/failure/pull/213 // provides a deref: https://github.com/rust-lang-nursery/failure/pull/213
let compat: Option<&failure::Compat<failure::Error>> = Fail::downcast_ref(self.cause.as_fail()); let compat: Option<&failure::Compat<failure::Error>> =
Fail::downcast_ref(self.cause.as_fail());
if let Some(compat) = compat { if let Some(compat) = compat {
pub struct CompatWrappedError { pub struct CompatWrappedError {
error: failure::Error, error: failure::Error,
} }
let compat: &CompatWrappedError = unsafe { let compat: &CompatWrappedError = unsafe { ::std::mem::transmute(compat) };
::std::mem::transmute(compat)
};
compat.error.downcast_ref() compat.error.downcast_ref()
} else { } else {
None None
@ -113,8 +115,8 @@ impl Error {
/// Helper trait to downcast a response error into a fail. /// Helper trait to downcast a response error into a fail.
/// ///
/// This is currently not exposed because it's unclear if this is the best way to /// This is currently not exposed because it's unclear if this is the best way
/// achieve the downcasting on `Error` for which this is needed. /// to achieve the downcasting on `Error` for which this is needed.
#[doc(hidden)] #[doc(hidden)]
pub trait InternalResponseErrorAsFail { pub trait InternalResponseErrorAsFail {
#[doc(hidden)] #[doc(hidden)]
@ -125,8 +127,12 @@ pub trait InternalResponseErrorAsFail {
#[doc(hidden)] #[doc(hidden)]
impl<T: ResponseError> InternalResponseErrorAsFail for T { impl<T: ResponseError> InternalResponseErrorAsFail for T {
fn as_fail(&self) -> &Fail { self } fn as_fail(&self) -> &Fail {
fn as_mut_fail(&mut self) -> &mut Fail { self } self
}
fn as_mut_fail(&mut self) -> &mut Fail {
self
}
} }
/// Error that can be converted to `HttpResponse` /// Error that can be converted to `HttpResponse`
@ -183,11 +189,9 @@ impl<T: ResponseError> From<T> for Error {
} }
/// Compatibility for `failure::Error` /// Compatibility for `failure::Error`
impl<T> ResponseError for failure::Compat<T> impl<T> ResponseError for failure::Compat<T> where
where T: fmt::Display + fmt::Debug + Sync + Send + 'static
T: fmt::Display + fmt::Debug + Sync + Send + 'static, {}
{
}
impl From<failure::Error> for Error { impl From<failure::Error> for Error {
fn from(err: failure::Error) -> Error { fn from(err: failure::Error) -> Error {
@ -620,8 +624,8 @@ impl From<UrlParseError> for UrlGenerationError {
/// use actix_web::fs::NamedFile; /// use actix_web::fs::NamedFile;
/// ///
/// fn index(req: HttpRequest) -> Result<fs::NamedFile> { /// fn index(req: HttpRequest) -> Result<fs::NamedFile> {
/// let f = NamedFile::open("test.txt").map_err(error::ErrorBadRequest)?; /// let f = NamedFile::open("test.txt").map_err(error::ErrorBadRequest)?;
/// Ok(f) /// Ok(f)
/// } /// }
/// # fn main() {} /// # fn main() {}
/// ``` /// ```
@ -631,12 +635,9 @@ pub struct InternalError<T> {
backtrace: Backtrace, backtrace: Backtrace,
} }
unsafe impl<T> Sync for InternalError<T> {}
unsafe impl<T> Send for InternalError<T> {}
enum InternalErrorType { enum InternalErrorType {
Status(StatusCode), Status(StatusCode),
Response(RefCell<Option<HttpResponse>>), Response(Mutex<Option<Box<InnerHttpResponse>>>),
} }
impl<T> InternalError<T> { impl<T> InternalError<T> {
@ -653,7 +654,7 @@ impl<T> InternalError<T> {
pub fn from_response(cause: T, response: HttpResponse) -> Self { pub fn from_response(cause: T, response: HttpResponse) -> Self {
InternalError { InternalError {
cause, cause,
status: InternalErrorType::Response(RefCell::new(Some(response))), status: InternalErrorType::Response(Mutex::new(Some(response.into_inner()))),
backtrace: Backtrace::new(), backtrace: Backtrace::new(),
} }
} }
@ -694,8 +695,8 @@ where
match self.status { match self.status {
InternalErrorType::Status(st) => HttpResponse::new(st), InternalErrorType::Status(st) => HttpResponse::new(st),
InternalErrorType::Response(ref resp) => { InternalErrorType::Response(ref resp) => {
if let Some(resp) = resp.borrow_mut().take() { if let Some(resp) = resp.lock().unwrap().take() {
resp HttpResponse::from_inner(resp)
} else { } else {
HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR) HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR)
} }

View file

@ -241,6 +241,14 @@ impl HttpResponse {
pub fn set_write_buffer_capacity(&mut self, cap: usize) { pub fn set_write_buffer_capacity(&mut self, cap: usize) {
self.get_mut().write_capacity = cap; self.get_mut().write_capacity = cap;
} }
pub(crate) fn into_inner(mut self) -> Box<InnerHttpResponse> {
self.0.take().unwrap()
}
pub(crate) fn from_inner(inner: Box<InnerHttpResponse>) -> HttpResponse {
HttpResponse(Some(inner), HttpResponsePool::pool())
}
} }
impl fmt::Debug for HttpResponse { impl fmt::Debug for HttpResponse {
@ -297,11 +305,13 @@ impl HttpResponseBuilder {
/// ///
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_web;
/// use actix_web::{HttpRequest, HttpResponse, Result, http}; /// use actix_web::{http, HttpRequest, HttpResponse, Result};
/// ///
/// fn index(req: HttpRequest) -> Result<HttpResponse> { /// fn index(req: HttpRequest) -> Result<HttpResponse> {
/// Ok(HttpResponse::Ok() /// Ok(HttpResponse::Ok()
/// .set(http::header::IfModifiedSince("Sun, 07 Nov 1994 08:48:37 GMT".parse()?)) /// .set(http::header::IfModifiedSince(
/// "Sun, 07 Nov 1994 08:48:37 GMT".parse()?,
/// ))
/// .finish()) /// .finish())
/// } /// }
/// fn main() {} /// fn main() {}
@ -455,7 +465,8 @@ impl HttpResponseBuilder {
/// .path("/") /// .path("/")
/// .secure(true) /// .secure(true)
/// .http_only(true) /// .http_only(true)
/// .finish()) /// .finish(),
/// )
/// .finish() /// .finish()
/// } /// }
/// fn main() {} /// fn main() {}
@ -781,7 +792,7 @@ impl<'a, S> From<&'a HttpRequest<S>> for HttpResponseBuilder {
} }
#[derive(Debug)] #[derive(Debug)]
struct InnerHttpResponse { pub(crate) struct InnerHttpResponse {
version: Option<Version>, version: Option<Version>,
headers: HeaderMap, headers: HeaderMap,
status: StatusCode, status: StatusCode,
@ -795,6 +806,9 @@ struct InnerHttpResponse {
error: Option<Error>, error: Option<Error>,
} }
unsafe impl Sync for InnerHttpResponse {}
unsafe impl Send for InnerHttpResponse {}
impl InnerHttpResponse { impl InnerHttpResponse {
#[inline] #[inline]
fn new(status: StatusCode, body: Body) -> InnerHttpResponse { fn new(status: StatusCode, body: Body) -> InnerHttpResponse {