Review unsafe code in `extend_mut` crate

Hello, I created a new crate called extend_mut, which allows you to temporary extend exclusive lifetime. Can you please review it for unsoundness, or maybe there already are alternative crates/patterns?

This is unsound in combination with crates which uses a ZST as a token for permission to access something. It's allowed to have two &muts to the same address for ZSTs, so the pointer equality check you did doesn't prevent the closure from storing the &mut it got somewhere globally.

1 Like

Thank you for a great catch! I will think about solution for this. I guess with generic_const_exprs there can be a trait implemented for size_of::<T>() != 0, or just add post-monomorphization const { assert!(size_of::<T>() != 0) }

It would probably be unsoud with Tracking Issue for RFC 3467: UnsafePinned · Issue #125735 · rust-lang/rust · GitHub too, as types with UnsafePinned inside are allowed to &mut alias. But I am not sure how to safely exploit this yet.

Thinking about it again, I'm not sure if the ZST thing is actually unsound. The best I could do is write this function, which I'm unsure whether it's sound or not.

use extend_mut::extend_mut;

// Given two references with the same address,
// returns a reference with the first one's provenance but the second one's lifetime.
fn silly<'a, 'b, T>(x: &'a mut T, y: &'b mut T) -> &'b mut T {
    assert!(size_of::<T>() == 0);
    assert!(std::ptr::eq(x, y));
    let mut x_holder: Option<&'b mut T> = None;
    extend_mut(x, |x_arg: &'b mut T| {
        x_holder = Some(x_arg);
        y
    });
    x_holder.unwrap()
}

I found a simpler unsoundness. This code segfaults

use extend_mut::{extend_mut, IntoExtendMutReturn};

struct Weird;
impl<'a, T> IntoExtendMutReturn<'a, T, i64> for Weird {
    fn into_extend_mut_return(self) -> (&'a mut T, i64) {
        panic!()
    }
}

struct PrintOnDrop<'a>(Option<&'a Box<i32>>);
impl Drop for PrintOnDrop<'_> {
    fn drop(&mut self) {
        println!("Dropping: {:?}", self.0);
    }
}

fn main() {
    let mut holder = PrintOnDrop(None);
    {
        let mut x = Box::new(Box::new(123));
        extend_mut(&mut *x, |x_arg| {
            holder.0 = Some(x_arg);
            Weird
        });
    }
}
2 Likes

Oh, I forgot to seal that trait :sweat_smile: . Nice catch!

I think nesting two extend_mut may be a way to zst exploit