Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tricky soundness issue #122

Open
steffahn opened this issue Sep 10, 2024 · 0 comments
Open

Tricky soundness issue #122

steffahn opened this issue Sep 10, 2024 · 0 comments

Comments

@steffahn
Copy link
Contributor

Or… “with_mut is still unsound” (in reference to #98).

Not my title of choice though, because I’m not sure yet if I would actually blame with_mut here.

Because the below code also relies on an invariant field, and a field with destructor; both together on the same field would be prevented by check_if_okay_according_to_checkers . Of course, that’s probably supposed to be fine, usually. Just not in all cases.

A soundness fix might be as simple as adding a step constructing a BorrowedMutFields to check_if_okay_according_to_checkers; I have not tested this yet though – neither whether it’s sufficient, not whether it breaks too much code.

use std::cell::Cell;
use std::fmt;

use ouroboros::self_referencing;

struct PrintOnDrop<T: fmt::Display>(T);
impl<T: fmt::Display> Drop for PrintOnDrop<T> {
    fn drop(&mut self) {
        println!("printing on drop... {}", self.0);
    }
}

#[self_referencing]
struct Problem {
    fake_owner: (),

    #[borrows(fake_owner)]
    #[not_covariant]
    first_dependent: Cell<&'this str>,

    #[borrows(fake_owner)]
    #[covariant]
    second_dependent: Option<PrintOnDrop<&'this str>>,

    owner: String,

    #[borrows(owner)]
    fake_dependent: (),
}

fn main() {
    let mut s = ProblemBuilder {
        fake_owner: (),
        first_dependent_builder: |_| Cell::new(""),
        second_dependent_builder: |_| None,
        owner: String::from("Hi there, fellow Rustaceans!"),
        fake_dependent_builder: |_| (),
    }
    .build();

    s.with(|s| s.first_dependent.set(s.owner));

    s.with_mut(|s| *s.second_dependent = Some(PrintOnDrop(s.first_dependent.get())));

    // on drop, `owner` is dropped before `second_dependent`,
    // so `Drop for PrintOnDrop` execution runs into use-after-free
}
printing on drop... �T~:cbp�"\#u) Rustaceans!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant