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

Commit

Permalink
Fix a memory leak in downcast (#262)
Browse files Browse the repository at this point in the history
  • Loading branch information
mitsuhiko committed Oct 1, 2018
1 parent 1657af3 commit 345cb5a
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -7,6 +7,7 @@ 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: 39 additions & 0 deletions build.rs
@@ -0,0 +1,39 @@
use std::env;
use std::process::Command;
use std::str;
use std::str::FromStr;

fn main() {
if rustc_has_global_alloc() {
println!("cargo:rustc-cfg=has_global_alloc");
}
}

fn rustc_has_global_alloc() -> bool {
let rustc = match env::var_os("RUSTC") {
Some(rustc) => rustc,
None => return false,
};

let output = match Command::new(rustc).arg("--version").output() {
Ok(output) => output,
Err(_) => return false,
};

let version = match str::from_utf8(&output.stdout) {
Ok(version) => version,
Err(_) => return false,
};

let mut pieces = version.split('.');
if pieces.next() != Some("rustc 1") {
return true;
}

let next = match pieces.next() {
Some(next) => next,
None => return false,
};

u32::from_str(next).unwrap_or(0) >= 28
}
20 changes: 17 additions & 3 deletions src/error/error_impl.rs
@@ -1,4 +1,3 @@
use core::mem;
use core::ptr;

use Fail;
Expand Down Expand Up @@ -49,8 +48,23 @@ impl ErrorImpl {
});
match ret {
Some(ret) => {
// forget self (backtrace is dropped, failure is moved
mem::forget(self);
// 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)
Expand Down
10 changes: 10 additions & 0 deletions src/error/mod.rs
Expand Up @@ -122,6 +122,9 @@ 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 @@ -225,4 +228,11 @@ mod test {
drop(error);
assert!(true);
}

#[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();
assert_eq!(real_io_error.to_string(), "test");
}
}

0 comments on commit 345cb5a

Please sign in to comment.