qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [RFC PATCH v5 0/8] Add Rust support, implement ARM PL011


From: Paolo Bonzini
Subject: Re: [RFC PATCH v5 0/8] Add Rust support, implement ARM PL011
Date: Thu, 25 Jul 2024 17:15:00 +0200

On Thu, Jul 25, 2024 at 4:48 PM Manos Pitsidianakis
<manos.pitsidianakis@linaro.org> wrote:
> > pl011_receive (called by qemu_chr_fe_accept_input) creates a mutable
> > reference that *overlaps* the lifetime of the outer reference created
> > by pl011_read. This is undefined behavior. You're effectively writing:
>
> There is no overlap there, sorry. Once qemu_chr_fe_accept_input
> returns, any references it created do not exist anymore.

read     |-------------|
receive      |-----|

That's the overlap. Maybe you're thinking that the outer &mut "goes to
sleep" and is reborn when qemu_chr_fe_accept_input() returns, but
there's no such thing in the language.

You can do it within a function:

fn main() {
  let mut x = 42u32;
  let a = &mut x;
  let b = &mut *a;
  *b = 0;
  *a = 43;
  // *b = 42;
}

But that's because the compiler _knows_ the provenance of b. So it
says "ok, 'b' is basically the same as 'a' and 'a' won't be accessed
while 'b' is live". This doesn't happen in any other case. In fact
miri points out that, the moment you create a &mut in pl011_read, the
chardev's opaque becomes effectively unusable[1]. In other words,
you're not even allowed to turn it from a *mut into a &mut!

Again: it's exactly the same as the example above

fn main() {
    let mut foo = String::from("foo");
    let x = &mut foo;                         // pl011_read has a reference
    let x_ptr: *mut String = x as *mut _;     // pl011_receive starts
    let y = &mut foo;                         // pl011_receive creates
a reference

    // at this point, x becomes invalid
    print!("{}", y);
    // pl011_receive returns

    // this is undefined behavior (shown by miri)
    print!("{}", unsafe { &mut *x_ptr });
}

Honestly I don't see how I can provide a proof or explanation that is
more definitive than miri. If some specific documentation or
discussions gives you the perception that there is no undefined
behavior, I'm happy to check them out and provide an explanation in
that context. But otherwise, I don't think it's useful to keep
debating *whether* it is undefined behavior.

Paolo

[1] this is the other example I gave a few hours ago, which I'll copy
here for reference:

// MIRIFLAGS=-Zmiri-ignore-leaks  cargo +nightly miri run
use std::mem::MaybeUninit;

struct S {
    me: *mut S,
    them: *mut S
}

impl S {
    pub fn chardev_receive(&mut self) {
        println!("in chardev_receive with &mut");
    }

    pub fn memory_write_good(&mut self) {
        println!("in memory_write_good, calling qemu_chr_fe_accept_input()");
        qemu_chr_fe_accept_input_good(self);
    }

    pub fn memory_write_bad(&mut self) {
        println!("in memory_write_bad, calling qemu_chr_fe_accept_input()");
        qemu_chr_fe_accept_input_bad(self);
    }
}

fn qemu_chr_fe_accept_input_good(c: &S) {
    // you can still go from *mut to &mut in _another_ instance of struct S
    (unsafe { &mut *c.them }).chardev_receive();
}

fn qemu_chr_fe_accept_input_bad(c: &S) {
    // you cannot go from *mut to &mut when it points to _this_ instance;
    // creating the &mut that is passed to memory_write_bad() has
    // effectively made that *mut unusable!
    (unsafe { &mut *c.me }).chardev_receive();
}

fn main() {
    let p: &mut MaybeUninit<S> = Box::leak(Box::new(MaybeUninit::uninit()));
    let q: &mut MaybeUninit<S> = Box::leak(Box::new(MaybeUninit::uninit()));

    unsafe {
        let p_mut_ptr = p.as_mut_ptr();
        let q_mut_ptr = q.as_mut_ptr();
        *(&mut *p_mut_ptr) = S { me: p_mut_ptr, them: q_mut_ptr };
        (&mut *p_mut_ptr).memory_write_bad();
        (&mut *p_mut_ptr).memory_write_good();
   }
}




reply via email to

[Prev in Thread] Current Thread [Next in Thread]