2
votes

I'd like to write a safe Rust wrapper for a C library. I need to express the C's library raw pointer ownership rules in Rust's terms.

The library has its private structure such as: struct handle {void *_data} and exposes setter as set_data(struct handle*, void *data).

I'd like to make Rust version of that method with signature that says "data must live at least as long as the handle".

I've tried:

set_data(&'a self, &'a data:…)

but borrow checker seems to apply that to lifetime within that function, not overall lifetime of the object.

I've also tried to add lifetime to impl, but that's still no good. Full test case:

#![allow(unused_variables)]
struct Handle<'a>;

impl<'a> Handle<'a> {
    pub fn set_data(&'a mut self, data: &'a DropCanary) {
        // save data raw ptr
    }

    pub fn use_data(&'a self) {
        // use data raw ptr
        println!("alive?");
    }
}

fn main() {
    let mut handle = Handle;
    let long_enough_lifetime = DropCanary{label:"long"};

    {
        let short_lifetime = DropCanary{label:"short"};
        handle.set_data(&short_lifetime); // This shouldn't be allowed!
        handle.set_data(&long_enough_lifetime); // This is OK
    }

    handle.use_data();
}

/// --- just for testing ---

struct DropCanary {
    label: &'static str,
}

impl Drop for DropCanary {
    fn drop(&mut self) {
        println!("dropped: {}", self.label);
    }
}

The problem is that the following code compiles and outputs:

dropped: short
alive?
dropped: long

So it causes use-after-free, because Rust doesn't know that short_lifetime must outlive handle.

1

1 Answers

1
votes

This should work for you (playpen).

The reason that your example compiles is simply because you do not use the lifetime inside the struct inside your example. Since you do not use it there is no constraint on it and it could just as well have been omitted. If you do not store any data with a lifetime inside your struct there is marker types which you can substitute for the Option I used here.

#![allow(unused_variables)]
struct Handle<'a>(Option<&'a DropCanary>);

impl<'a> Handle<'a> {
    pub fn set_data(&mut self, data: &'a DropCanary) {
        self.0 = Some(data);
        // save data raw ptr
    }

    pub fn use_data(&self) {
        // use data raw ptr
        println!("alive?");
    }
}

fn main() {
    let mut handle = Handle(None);
    let long_enough_lifetime = DropCanary{label:"long"};

    {
        let short_lifetime = DropCanary{label:"short"};
        //handle.set_data(&short_lifetime); // This shouldn't be allowed!
        handle.set_data(&long_enough_lifetime); // This is OK
    }

    handle.use_data();
}

/// --- just for testing ---

struct DropCanary {
    label: &'static str,
}

impl Drop for DropCanary {
    fn drop(&mut self) {
        println!("dropped: {}", self.label);
    }
}