Skip to content
This repository was archived by the owner on Aug 16, 2021. It is now read-only.

Commit 47c4ce5

Browse files
rustonautmitsuhiko
authored andcommitted
Fix memory leak in downcast for older rust versions (#262). (#265)
The previous fix didn't work for all supported rust versions as it was relying on the alloc fn's.
1 parent 5cb0324 commit 47c4ce5

File tree

4 files changed

+19
-77
lines changed

4 files changed

+19
-77
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ license = "MIT OR Apache-2.0"
77
name = "failure"
88
repository = "https://github.com/rust-lang-nursery/failure"
99
version = "0.1.2"
10-
build = "build.rs"
1110

1211
[dependencies.failure_derive]
1312
optional = true

build.rs

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/error/error_impl.rs

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use core::ptr;
1+
use core::any::TypeId;
22

33
use Fail;
44
use backtrace::Backtrace;
@@ -38,36 +38,13 @@ impl ErrorImpl {
3838
}
3939

4040
pub(crate) fn downcast<T: Fail>(self) -> Result<T, ErrorImpl> {
41-
let ret: Option<T> = self.failure().downcast_ref().map(|fail| {
42-
unsafe {
43-
// drop the backtrace
44-
let _ = ptr::read(&self.inner.backtrace as *const Backtrace);
45-
// read out the fail type
46-
ptr::read(fail as *const T)
47-
}
48-
});
49-
match ret {
50-
Some(ret) => {
51-
// deallocate the box without dropping the inner parts
52-
#[cfg(has_global_alloc)] {
53-
use std::alloc::{dealloc, Layout};
54-
unsafe {
55-
let layout = Layout::for_value(&*self.inner);
56-
let ptr = Box::into_raw(self.inner);
57-
dealloc(ptr as *mut u8, layout);
58-
}
59-
}
60-
61-
// slightly leaky versions of the above thing which makes the box
62-
// itself leak. There is no good way around this as far as I know.
63-
#[cfg(not(has_global_alloc))] {
64-
use core::mem;
65-
mem::forget(self);
66-
}
67-
68-
Ok(ret)
69-
}
70-
_ => Err(self)
41+
if self.failure().__private_get_type_id__() == TypeId::of::<T>() {
42+
let ErrorImpl { inner } = self;
43+
let casted = unsafe { Box::from_raw(Box::into_raw(inner) as *mut Inner<T>) };
44+
let Inner { backtrace:_, failure } = *casted;
45+
Ok(failure)
46+
} else {
47+
Err(self)
7148
}
7249
}
7350
}

src/error/mod.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,6 @@ impl Error {
122122
/// failure is of the type `T`. For this reason it returns a `Result` - in
123123
/// the case that the underlying error is of a different type, the
124124
/// original `Error` is returned.
125-
///
126-
/// Note that this method leaks on Rust versions < 1.28.0.
127-
#[cfg_attr(not(has_global_alloc), deprecated(note = "this method leaks on Rust versions < 1.28"))]
128125
pub fn downcast<T: Fail>(self) -> Result<T, Error> {
129126
self.imp.downcast().map_err(|imp| Error { imp })
130127
}
@@ -230,9 +227,17 @@ mod test {
230227
}
231228

232229
#[test]
233-
fn test_downcast() {
234-
let error: Error = io::Error::new(io::ErrorKind::NotFound, "test").into();
235-
let real_io_error = error.downcast_ref::<io::Error>().unwrap();
230+
fn downcast_can_be_used() {
231+
let mut error: Error = io::Error::new(io::ErrorKind::NotFound, "test").into();
232+
{
233+
let real_io_error_ref = error.downcast_ref::<io::Error>().unwrap();
234+
assert_eq!(real_io_error_ref.to_string(), "test");
235+
}
236+
{
237+
let real_io_error_mut = error.downcast_mut::<io::Error>().unwrap();
238+
assert_eq!(real_io_error_mut.to_string(), "test");
239+
}
240+
let real_io_error = error.downcast::<io::Error>().unwrap();
236241
assert_eq!(real_io_error.to_string(), "test");
237242
}
238243
}

0 commit comments

Comments
 (0)