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

Possible unsoudness while using RwlockWriteGuard::map and RwlockReadGuard::try_map #4181

Closed
pvdrz opened this issue Oct 19, 2021 · 3 comments
Labels
A-tokio Area: The main tokio crate C-bug Category: This is a bug. M-sync Module: tokio/sync

Comments

@pvdrz
Copy link

pvdrz commented Oct 19, 2021

Version

$ cargo tree | grep tokio
└── tokio v1.12.0
    └── tokio-macros v1.5.0 (proc-macro)

Platform

$ uname -a
Linux exuberant-witness 5.14.12-zen1-1-zen #1 ZEN SMP PREEMPT Wed, 13 Oct 2021 16:58:18 +0000 x86_64 GNU/Linux

Description
I found a possible unsoundness using miri while writing a lock-based concurrent hashmap. This is the minimum example I was able to isolate:

use tokio::{
    sync::{RwLock, RwLockMappedWriteGuard, RwLockReadGuard, RwLockWriteGuard},
    time::{sleep, Duration},
};

use std::{collections::HashMap, hash::Hash, sync::Arc};

struct Map<K, V> {
    inner: RwLock<HashMap<K, V>>,
}

impl<K: Hash + Eq, V: Default> Map<K, V> {
    fn new() -> Self {
        Self {
            inner: RwLock::new(HashMap::new()),
        }
    }

    async fn get(&self, key: &K) -> Option<RwLockReadGuard<'_, V>> {
        RwLockReadGuard::try_map(self.inner.read().await, |map| map.get(key)).ok()
    }

    async fn get_mut_or_default(&self, key: K) -> RwLockMappedWriteGuard<'_, V> {
        RwLockWriteGuard::map(self.inner.write().await, |map| map.entry(key).or_default())
    }
}

#[tokio::main]
async fn main() {
    let map = Arc::new(Map::<String, String>::new());
    let key = "hello".to_string();

    let task1 = {
        let map = Arc::clone(&map);
        let key = key.clone();

        tokio::spawn(async move {
            let vertex = map.get_mut_or_default(key).await;
            sleep(Duration::from_secs(5)).await;
            drop(vertex);
        })
    };

    let task2 = {
        let map = Arc::clone(&map);
        let key = key.clone();

        tokio::spawn(async move {
            map.get(&key).await;
        })
    };

    let (r1, r2) = tokio::join!(task1, task2);

    r1.unwrap();
    r2.unwrap();
}

When running env MIRIFLAGS="-Zmiri-disable-isolation" cargo +nightly miri run i get this output:

error: Undefined Behavior: trying to reborrow for SharedReadOnly at alloc24055+0x48, but parent tag <68276> does not have an appropriate item in the borrow stack
  --> src/main.rs:20:73
   |
20 |         RwLockReadGuard::try_map(self.inner.read().await, |map| map.get(key)).ok()
   |                                                                         ^^^ trying to reborrow for SharedReadOnly at alloc24055+0x48, but parent tag <68276> does not have an appropriate item in the borrow stack
   |
   = 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 closure at src/main.rs:20:73

I wonder is this is related to #3344

@pvdrz pvdrz added A-tokio Area: The main tokio crate C-bug Category: This is a bug. labels Oct 19, 2021
@Darksonn Darksonn added the M-sync Module: tokio/sync label Oct 19, 2021
@Darksonn
Copy link
Contributor

It definitely isn't related to #3344. That issue got fixed.

Anyway, my guess is that this isn't the RwLock, but simply that async/await syntax is unsound under the stacked borrows model. See rust-lang/rust#63818 for more info.

@pvdrz
Copy link
Author

pvdrz commented Oct 19, 2021

Oh OK i wasn't aware of that. Should we close this?

@Darksonn
Copy link
Contributor

Unless you are going to investigate further to determine the exact cause, then I think we can close it.

@pvdrz pvdrz closed this as completed Oct 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tokio Area: The main tokio crate C-bug Category: This is a bug. M-sync Module: tokio/sync
Projects
None yet
Development

No branches or pull requests

2 participants