Skip to content

MutexGuard<Cell<i32>> must not be Sync #41622

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
RalfJung opened this issue Apr 29, 2017 · 2 comments
Closed

MutexGuard<Cell<i32>> must not be Sync #41622

RalfJung opened this issue Apr 29, 2017 · 2 comments
Labels
I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness

Comments

@RalfJung
Copy link
Member

RalfJung commented Apr 29, 2017

Right now, MutexGuard<Cell<i32>> satisfies the Sync bound. That is rather bad, because it lets me write a program that has a data race:

use std::sync::Mutex;
use std::cell::Cell;

extern crate rayon;

fn main()
{
    let m = Mutex::new(Cell::new(0));
    let g = m.lock().unwrap();
    {
        rayon::join(
            || { g.set(g.get() + 1); println!("Thread 1: {:?}", g.get()) },
            || { g.set(g.get() + 1); println!("Thread 2: {:?}", g.get()) });
    }
}

The get and set calls in the two threads are unsynchronized (as usual for a Cell), and they are racing. This is a soundness bug.

The cause for this is that MutexGuard<T> implements Sync whenever T implements Send, which is plain wrong. The fix is to let MutexGuard<T> implement Sync whenever T implements Sync. I will submit a PR soon.

@RalfJung RalfJung changed the title MutexGuard<Cell<i32>> must not be sync MutexGuard<Cell<i32>> must not be Sync Apr 29, 2017
@RalfJung
Copy link
Member Author

Btw, this bug was found while trying to prove soundnes of (an idealized version of) Mutex. Yay for formal methods :D

Also, this hints at a more general problem: OIBITS for types with custom invariants ("unsafe types") are dangerous.

@RalfJung
Copy link
Member Author

Also, this hints at a more general problem: OIBITS for types with custom invariants ("unsafe types") are dangerous.

To elaborate on this (from a formal proof perspective): To prove correctness of Send/Sync for an unsafe type (i.e., a type with its own invariant), I need to know exactly under which bounds the type is Send/Sync -- this defines the theorem I have to prove. However, if the type does not have an explicit impl for Send/Sync, I don't know how to even figure this out -- I would have to chase all types (including safe ones, and across all abstractions) of all fields recursively and then check when they are Send/Sync... that's way too error-prone. What I do instead is I try to compile small programs that only work e.g. if T: Send implies MutexGuard<T>: Sync. However, I may easily miss impls this way -- maybe we need T: Send+'static to get MutexGuard<T>: Sync? It is impossible to cover all possible constraints.

Also, even if there is an impl, there are still some subtleties: First of all, does giving a restrictive positive impl disable the more liberal automatic impl? (Tests indicate yes.) What if there is a restrictve negative impl? If I find impl<T: 'static> !Sync for MutexGuard<T>, does the automatic impl still apply for types that do not satisfy T:'static? (I haven't yet seen such a restrictive negative impl, so I don't know -- but this can happen accidentally with ?Sized if the type has T: ?Sized but the negative impl just has T.)

@withoutboats withoutboats added the I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness label Apr 29, 2017
bors added a commit that referenced this issue May 3, 2017
MutexGuard<T> may be Sync only if T is Sync

Fixes #41622

This is a breaking change. Does that imply any further process?

I am not sure whether I wrote that "compilation must fail"-test correctly, but at least it is passing here with the patch applied. Same for the `since = "1.18.0"`, I just picked it because I had to pick something.
fbornhofen added a commit to fbornhofen/comprehensive-rust that referenced this issue Oct 12, 2023
This example only makes sense (and is therefore easier to understand logically :) ) if T is Sync.
See rust-lang/rust#41622 - this used to be a bug initially.
sakex pushed a commit to google/comprehensive-rust that referenced this issue Oct 12, 2023
This example only makes sense (and is therefore easier to understand
logically :) ) if T is Sync. See
rust-lang/rust#41622 - this used to be a bug
initially.
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
Projects
None yet
Development

No branches or pull requests

2 participants