Skip to content

Commit

Permalink
Fix memory leak in downcast for older rust versions (rust-lang-deprec…
Browse files Browse the repository at this point in the history
…ated#262).

The previous fix didn't work for all supported
rust versions as it was relying on the alloc fn's.
  • Loading branch information
rustonaut committed Oct 2, 2018
1 parent 5cb0324 commit 32324e7
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 77 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ license = "MIT OR Apache-2.0"
name = "failure"
repository = "https://github.com/rust-lang-nursery/failure"
version = "0.1.2"
build = "build.rs"

[dependencies.failure_derive]
optional = true
Expand Down
39 changes: 0 additions & 39 deletions build.rs

This file was deleted.

39 changes: 8 additions & 31 deletions src/error/error_impl.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use core::ptr;
use core::any::TypeId;

use Fail;
use backtrace::Backtrace;
Expand Down Expand Up @@ -38,36 +38,13 @@ impl ErrorImpl {
}

pub(crate) fn downcast<T: Fail>(self) -> Result<T, ErrorImpl> {
let ret: Option<T> = self.failure().downcast_ref().map(|fail| {
unsafe {
// drop the backtrace
let _ = ptr::read(&self.inner.backtrace as *const Backtrace);
// read out the fail type
ptr::read(fail as *const T)
}
});
match ret {
Some(ret) => {
// deallocate the box without dropping the inner parts
#[cfg(has_global_alloc)] {
use std::alloc::{dealloc, Layout};
unsafe {
let layout = Layout::for_value(&*self.inner);
let ptr = Box::into_raw(self.inner);
dealloc(ptr as *mut u8, layout);
}
}

// slightly leaky versions of the above thing which makes the box
// itself leak. There is no good way around this as far as I know.
#[cfg(not(has_global_alloc))] {
use core::mem;
mem::forget(self);
}

Ok(ret)
}
_ => Err(self)
if self.failure().__private_get_type_id__() == TypeId::of::<T>() {
let ErrorImpl { inner } = self;
let casted = unsafe { Box::from_raw(Box::into_raw(inner) as *mut Inner<T>) };
let Inner { backtrace:_, failure } = *casted;
Ok(failure)
} else {
Err(self)
}
}
}
17 changes: 11 additions & 6 deletions src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,6 @@ impl Error {
/// failure is of the type `T`. For this reason it returns a `Result` - in
/// the case that the underlying error is of a different type, the
/// original `Error` is returned.
///
/// Note that this method leaks on Rust versions < 1.28.0.
#[cfg_attr(not(has_global_alloc), deprecated(note = "this method leaks on Rust versions < 1.28"))]
pub fn downcast<T: Fail>(self) -> Result<T, Error> {
self.imp.downcast().map_err(|imp| Error { imp })
}
Expand Down Expand Up @@ -230,9 +227,17 @@ mod test {
}

#[test]
fn test_downcast() {
let error: Error = io::Error::new(io::ErrorKind::NotFound, "test").into();
let real_io_error = error.downcast_ref::<io::Error>().unwrap();
fn downcast_can_be_used() {
let mut error: Error = io::Error::new(io::ErrorKind::NotFound, "test").into();
{
let real_io_error_ref = error.downcast_ref::<io::Error>().unwrap();
assert_eq!(real_io_error_ref.to_string(), "test");
}
{
let real_io_error_mut = error.downcast_mut::<io::Error>().unwrap();
assert_eq!(real_io_error_mut.to_string(), "test");
}
let real_io_error = error.downcast::<io::Error>().unwrap();
assert_eq!(real_io_error.to_string(), "test");
}
}

0 comments on commit 32324e7

Please sign in to comment.