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

Mutex::lock_noguard() may be unsafe #862

Open
fbq opened this issue Aug 6, 2022 · 4 comments
Open

Mutex::lock_noguard() may be unsafe #862

fbq opened this issue Aug 6, 2022 · 4 comments
Labels
• lib Related to the `rust/` library.

Comments

@fbq
Copy link
Member

fbq commented Aug 6, 2022

@wedsonaf This came to me when I'm investigating the safety of schedule.

Linux's mutex_lock() sets ->lock to the current task once the lock is grabbed. If another thread tries to grab the lock, instead of sleeping immediately, it will check whether the lock owner is on cpu or not (mutex is a sleepable lock) via task_struct::on_cpu. And if the task is running on some CPU, the owner may finish its work soon and release the lock, so the other thread will spin instead of sleep.

Consider the following case

let m: Ref<Mutex<...>>= ...;
let m_a = m.clone();
let m_b = m.clone();

Task::spawn(move || {  // spawn "t1"
    let _ = m_a.lock_noguad(); // m's owner is "t1"
});

// "t1" gets freed. but m's owner is still "t1"

Task::spawn(move || { // spawn "t2"
    m_b.lock_noguard();
      mutex_lock():
        __mutex_lock_common():
          mutex_optimistic_spin():
            mutex_spin_on_owner():
              owner_on_cpu():
                <read owner->on_cpu> // UAF, unsafe

This means Mutex::lock_noguard() is unsafe, and the safety guarantee would be making sure that a thread releases the lock before exiting or #863 is needed

@fbq fbq changed the title Mutex::lock_noguard() may be unsaffe Mutex::lock_noguard() may be unsafe Aug 6, 2022
@wedsonaf
Copy link
Member

wedsonaf commented Aug 9, 2022

Very cool find, @fbq !

I'm not a big fan of the idea of having extra atomic incs/decs on the lock/unlock paths as that would clearly make the Rust version more expensive. We may have to do it though if we can't find another way of dealing with this. Let me think about this for a bit.

@fbq
Copy link
Member Author

fbq commented Aug 11, 2022

Very cool find, @fbq !

I'm not a big fan of the idea of having extra atomic incs/decs on the lock/unlock paths as that would clearly make the Rust version more expensive. We may have to do it though if we can't find another way of dealing with this. Let me think about this for a bit.

I share the same worry about having extra atomic incs/decs. Maybe we just make lock_noguard unsafe, because most of the lock users will use Guard-API, plus if a user use lock_noguard, the unsafe unlock will also be used, in other words, still need to engage with some unsafety.

@filip-hejsek
Copy link

I think the Guard API is also unsafe because one can mem::forget the Guard (or leak it in a reference cycle).

@fbq
Copy link
Member Author

fbq commented Aug 22, 2022

I think the Guard API is also unsafe because one can mem::forget the Guard (or leak it in a reference cycle).

Ah, you are right.

y86-dev added a commit to y86-dev/linux that referenced this issue Sep 30, 2022
y86-dev added a commit to y86-dev/linux that referenced this issue Sep 30, 2022
y86-dev added a commit to y86-dev/linux that referenced this issue Oct 1, 2022
As seen in Rust-for-Linux#862 it is unsound to `mem::forget` a guard of a Mutex,
because the C mutex sets the thread owner.

It also is a footgun to send a `Guard` of a `Spinlock` to another thread
(as doing so might result in the spinlock being locked while the other
thread is sleeping).

It is a conservative decision to make all guard contexts `!Send`, this
can be lifted in the future.

Signed-off-by: Benno Lossin <y86-dev@protomail.com>
Co-authored-by: Miguel Ojeda <ojeda@users.noreply.github.com>
y86-dev added a commit to y86-dev/linux that referenced this issue Oct 1, 2022
As seen in Rust-for-Linux#862 it is unsound to `mem::forget` a guard of a Mutex,
because the C mutex sets the thread owner.

It also is a footgun to send a `Guard` of a `Spinlock` to another thread
(as doing so might result in the spinlock being locked while the other
thread is sleeping).

It is a conservative decision to make all guard contexts `!Send`, this
can be lifted in the future.

Link: Rust-for-Linux#862
Signed-off-by: Benno Lossin <y86-dev@protomail.com>
y86-dev added a commit to y86-dev/linux that referenced this issue Oct 1, 2022
As seen in [1] it is unsound to `mem::forget` a guard of a Mutex,
because the C mutex sets the thread owner.

Similarly it is a footgun to send a `Guard<Spinlock>` to another thread
(as doing so might result in the spinlock being locked while the other
thread is sleeping).

It is a conservative decision to prevent sending any `Guard` to another
thread.

Link: Rust-for-Linux#862 [1]
Signed-off-by: Benno Lossin <y86-dev@protonmail.com>
@ojeda ojeda added the • lib Related to the `rust/` library. label Feb 17, 2023
fbq referenced this issue in wedsonaf/linux Mar 21, 2023
This is a generic lock guard. It is written as a generic type because
the kernel has several locking primitives, and using a single guard type
allows for code reuse and for primitives like condition variables to
work on any locks that satisfy the `Lock` trait.

Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
• lib Related to the `rust/` library.
Development

No branches or pull requests

4 participants