I am getting memory allocated by an external C function. I then convert the memory to a [u8]
slice using std::slice::from_raw_parts()
. To avoid repeated calls to from_raw_parts()
I want to store the slice as a field. So far I have this.
struct ImageBuffer<'a> {
//Slice view of buffer allocated by C
pub bytes: &'a [u8],
}
But this is wrong right there. This says bytes
has a lifetime larger than the ImageBuffer
instance. This does not reflect the reality. The buffer is freed up from drop()
and hence bytes
should have a lifetime same as the struct instance. How do I model that?
With the current code it is easy to use after free.
impl <'a> ImageBuffer<'a> {
pub fn new() -> ImageBuffer<'a> {
let size: libc::size_t = 100;
let ptr = unsafe {libc::malloc(size)};
let bytes = unsafe {std::slice::from_raw_parts(ptr as *const u8, size)};
ImageBuffer {
bytes,
}
}
}
impl <'a> Drop for ImageBuffer<'a> {
fn drop(&mut self) {
unsafe {
libc::free(self.bytes.as_ptr() as *mut libc::c_void)
};
}
}
fn main() {
let bytes;
{
let img = ImageBuffer::new();
bytes = img.bytes;
}
//Use after free!
println!("Size: {}. First: {}", bytes.len(), bytes[0]);
}
I can solve this problem by writing a getter function for bytes
.
First make bytes
private.
struct ImageBuffer<'a> {
bytes: &'a [u8],
}
Then write this getter method. This establishes the fact that the returned bytes has a lifetime same as the struct instance.
impl <'a> ImageBuffer<'a> {
pub fn get_bytes(&'a self) -> &'a [u8] {
self.bytes
}
}
Now, a use after free will not be allowed. The following will not compile.
fn main() {
let bytes;
{
let img = ImageBuffer::new();
bytes = img.get_bytes();
}
println!("Size: {}. First: {}", bytes.len(), bytes[0]);
}
I find this solution deeply disturbing. The struct declaration is still conveying a wrong meaning (it still says bytes
has a larger lifetime than the struct instance). The get_bytes()
method counters that and conveys the correct meaning. I'm looking for an explanation of this situation and what the best way to handle it.