I'm banging my head against this supposedly simple usage of Box whilst trying to create some FFI helper code.
The sample here seems to give an error of free(): invalid pointer
when used with a struct that has a field.
pub struct Handle(usize);
impl Handle {
pub fn from<T>(obj: T) -> Self {
let boxed = Box::new(obj);
let mut ptr = Box::into_raw(boxed);
Self::from_ptr_mut(&mut ptr)
}
pub fn from_ptr_mut<T>(ptr: &mut T) -> Self {
Self(ptr as *mut T as usize)
}
pub fn to_box<T>(self) -> Box<T> {
let obj: *mut T = self.to_ptr_mut();
unsafe { Box::from_raw(obj) }
}
pub fn to_ptr_mut<T>(self) -> *mut T {
self.0 as *mut T
}
}
#[allow(dead_code)]
struct Crashes { value: u64 }
impl Drop for Crashes {
fn drop(&mut self) {
println!("Crashes dropped");
}
}
fn crashes() {
let t = Crashes { value: 12 };
let a = Handle::from(t);
let b = a.to_box::<Crashes>();
drop(b);
}
struct Works;
impl Drop for Works {
fn drop(&mut self) {
println!("Works dropped");
}
}
fn works() {
let t = Works;
let a = Handle::from(t);
let b = a.to_box::<Works>();
drop(b);
}
fn main() {
works();
crashes();
}
You can paste this into https://play.rust-lang.org/ and see how it throws aborts with the error free(): invalid pointer
The drop function seems to be called at the appropriate time, but the pointer seems to be somehow invalid
You end up creating a double pointer here:
impl Handle {
pub fn from<T>(obj: T) -> Self {
let boxed = Box::new(obj);
let mut ptr = Box::into_raw(boxed);
Self::from_ptr_mut(&mut ptr)
}
pub fn from_ptr_mut<T>(ptr: &mut T) -> Self {
Self(ptr as *mut T as usize)
}
...
}
Box::into_raw
returns a pointer, but then you take a mutable reference to that pointer, and store that address as a usize
. You should just be using the *mut T
as returned by Box::into_raw
.
The reason that the non-working code with the double pointer compiles is that your from<T>
and your from_ptr_mut<T>
can take entirely different T
parameters. If we consider the type T
passed to from<T>
to be a concrete type, then in this case you're calling from_ptr_mut<U>
(where U
is *mut T
) with an argument of type &mut *mut T
.
It should look like so:
impl Handle {
pub fn from<T>(obj: T) -> Self {
let boxed = Box::new(obj);
let ptr = Box::into_raw(boxed);
Self::from_ptr_mut(ptr)
}
pub fn from_ptr_mut<T>(ptr: *mut T) -> Self {
Self(ptr as usize)
}
...
}
Working example in the playground.
Even though we are in the realm of unsafe
you can have the compiler do some of the work for you by making the parameter T
be bound to your Handle
struct. This way you will be statically prevented from loading a different type than was stored.
Playground example where Handle includes a PhantomData.
In this second example you don't have to tell the compiler which item you're retrieving a la a.to_box::<Crashes>()
, which is good because you can't introduce undefined behavior by specifying the wrong type.
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