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

Fix unsound lock guard: Remove Send and Sync autotraits from the lock guard #8

Merged
merged 1 commit into from
Jan 6, 2023
Merged

Conversation

mbuesch
Copy link
Contributor

@mbuesch mbuesch commented May 29, 2022

Having Send on the lock guard is unsound. (Or at least it doesn't make any sense to send a lock guard to another thread).
Having Sync on the lock guard is unsound, unless T is Sync.

Normally this should use negative trait impls to get rid of Send and Sync.
However, negative trait impls are not fully stabilized, yet.
Use a phantom pointer instead. That's a bit overly strict in that it also removes Sync, if T is Sync.
But I think that's a low price to pay.

The following code exploits the bug:

use std::cell::Cell;
use std::sync::Arc;
use std::thread::sleep;
use std::time::Duration;
use try_lock::TryLock;

fn main() {
    let lock = Arc::new(TryLock::new(Cell::new(42_i32)));
    let guard = lock.try_lock().expect("try_lock failed.");

    const LOOP: usize = 10000;

    let _ = crossbeam::scope(|scope| {
        scope.spawn(|_| { // First thread
            for _ in 0..LOOP {
                guard.set(guard.get() + 1);
                sleep(Duration::from_micros(1));
            }
        });
        scope.spawn(|_| { // Second thread
            for _ in 0..LOOP {
                guard.set(guard.get() - 1);
                sleep(Duration::from_micros(1));
            }
        });
    });

    println!("{}", guard.get());
}

Without the bug fix, the printed value sometimes differs from the expected result 42 due to races.
With the bug fix applied, the code doesn't compile anymore. (as is expected, because the guard usage is unsound.)

The bug had also been present in stdlib MutexGuard. See this for a detailed explanation:

https://www.ralfj.de/blog/2017/06/09/mutexguard-sync.html

Having Send on the lock guard is unsound.
Having Sync on the lock guard is unsound, unless T is Sync.

Normally this should use negative trait impls to get rid of Send and Sync.
However, negative trait impls are not fully stabilized, yet.
Use a phantom pointer instead. That's a bit overly strict in that it also removes Sync, if T is Sync.
@mbuesch
Copy link
Contributor Author

mbuesch commented Sep 28, 2022

What do you think about this?
Does my analysis have any flaws?
Could this fix be improved in one way or another?

Thanks a lot for your support. :)

@seanmonstar seanmonstar merged commit 531bfce into seanmonstar:master Jan 6, 2023
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

Successfully merging this pull request may close these issues.

2 participants