Some C code calls into the Rust open
call below which returns a pointer. Later the C code passes the exact same pointer back to the close
function which tries to drop (free) it. It segfaults in free(3)
. Why?
use std::os::raw::{c_int, c_void};
struct Handle;
extern "C" fn open(_readonly: c_int) -> *mut c_void {
let h = Handle;
let h = Box::new(h);
return Box::into_raw(h) as *mut c_void;
}
extern "C" fn close(h: *mut c_void) {
let h = unsafe { Box::from_raw(h) };
// XXX This segfaults - why?
drop(h);
}
In close
, you end up creating a Box<c_void>
instead of a Box<Handle>
because you didn't cast the *mut c_void
back to *mut Handle
before invoking Box::from_raw
.
fn close(h: *mut c_void) {
let h = unsafe { Box::from_raw(h as *mut Handle) };
drop(h);
}
By the way, Box
doesn't actually allocate any memory for a zero-sized type (such as Handle
here) and uses a fixed, non-zero pointer value instead (which, in the current implementation, is the alignment of the type; a zero-sized type has an alignment of 1 by default). The destructor for a boxed zero-sized type knows not to try to deallocate memory at this fictitious memory address, but c_void
is not a zero-sized type (it has size 1), so the destructor for Box<c_void>
tries to free memory at address 0x1
, which causes the segfault.
The problem is you didn't cast the pointer back to a Handle
pointer while transforming it back to a Box
, and got a Box
of the wrong type.
This works:
fn close(h: *mut c_void) {
let h = unsafe { Box::from_raw(h as *mut Handle) };
// ^^^^^^^^^^^^^^
drop(h);
}
In your code, h
is a std::boxed::Box<std::ffi::c_void>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With