Skip to content

Stacked borrows: Mutable reference pops stack when created in some cases #2082

Closed as not planned
@eivindbergem

Description

@eivindbergem

I'm working on a statically allocated MPSC queue for embedded systems, and I've run into a miri issue. From my understanding of stacked borrows, reading from a mutable reference shouldn't pop from the stack. However, in my case the stack is popped when creating the reference, but only in some cases.

I've created a minimal example shown below.

Shared code:

use std::sync::atomic::{AtomicBool, Ordering};

struct Queue {
    field: AtomicBool,
}

impl Queue {
    fn new() -> Self {
        Self {
            field: AtomicBool::new(false),
        }
    }

    unsafe fn get_sender(&self) -> Sender {
        let this = &*(self as *const Self);

        Sender::new(&this.field)
    }

    fn recv(&mut self) {
        self.field.store(true, Ordering::Relaxed);
    }

    fn ref_mut(&mut self) -> &mut Self {
        self
    }
}

struct Sender {
    field: &'static AtomicBool,
}

impl Sender {
    fn new(field: &'static AtomicBool) -> Self {
        Self { field }
    }

    fn send(&self) {
        self.field.store(true, Ordering::Relaxed);
    }
}

This is fine:

fn main() {
    let mut queue = Queue::new();

    let sender = unsafe { queue.get_sender() };

    queue.recv();
    sender.send();
}

And this is also fine:

fn main() {
    let mut queue = Queue::new();

    let sender = unsafe { queue.get_sender() };
    let queue = queue.ref_mut();

    queue.recv();
    sender.send();
}

But this is not:

fn main() {
    let mut queue = Queue::new();

    let sender = unsafe { queue.get_sender() };
    let queue = &mut queue;

    queue.recv();
    sender.send();
}

Error (with tag tracking):

note: tracking was triggered
  --> src/main.rs:35:16
   |
35 |         Self { field }
   |                ^^^^^ created tag 2411
   |
   = note: inside `Sender::new` at src/main.rs:35:16
note: inside `Queue::get_sender` at src/main.rs:17:9
  --> src/main.rs:17:9
   |
17 |         Sender::new(&this.field)
   |         ^^^^^^^^^^^^^^^^^^^^^^^^
note: inside `main` at src/main.rs:46:27
  --> src/main.rs:46:27
   |
46 |     let sender = unsafe { queue.get_sender() };
   |                           ^^^^^^^^^^^^^^^^^^

note: tracking was triggered
  --> src/main.rs:48:17
   |
48 |     let queue = &mut queue;
   |                 ^^^^^^^^^^ popped tracked tag for item [SharedReadWrite for <2411>] due to Write access for <2392>
   |
   = note: inside `main` at src/main.rs:48:17

error: Undefined Behavior: trying to reborrow <2411> for SharedReadWrite permission at alloc1283[0x0], but that tag does not exist in the borrow stack for this location
  --> src/main.rs:39:9
   |
39 |         self.field.store(true, Ordering::Relaxed);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |         |
   |         trying to reborrow <2411> for SharedReadWrite permission at alloc1283[0x0], but that tag does not exist in the borrow stack for this location
   |         this error occurs as part of a reborrow at alloc1283[0x0..0x1]
   |
   = help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental
   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information

   = note: inside `Sender::send` at src/main.rs:39:9
note: inside `main` at src/main.rs:52:5
  --> src/main.rs:52:5
   |
52 |     sender.send();
   |     ^^^^^^^^^^^^^

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions