]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - kernel/futex.c
futex: Provide state handling for exec() as well
[mirror_ubuntu-bionic-kernel.git] / kernel / futex.c
index 8bfb4c0f3f5181d0ca587f6ff5692b313f0833cb..1857bb50b6cb92036d9b9e9b5343181740aa3be4 100644 (file)
@@ -3684,7 +3684,7 @@ static void exit_robust_list(struct task_struct *curr)
        }
 }
 
-void futex_exec_release(struct task_struct *tsk)
+static void futex_cleanup(struct task_struct *tsk)
 {
        if (unlikely(tsk->robust_list)) {
                exit_robust_list(tsk);
@@ -3724,23 +3724,56 @@ void futex_exit_recursive(struct task_struct *tsk)
        tsk->futex_state = FUTEX_STATE_DEAD;
 }
 
-void futex_exit_release(struct task_struct *tsk)
+static void futex_cleanup_begin(struct task_struct *tsk)
 {
-       tsk->futex_state = FUTEX_STATE_EXITING;
        /*
-        * Ensure that all new tsk->pi_lock acquisitions must observe
-        * FUTEX_STATE_EXITING. Serializes against attach_to_pi_owner().
-        */
-       smp_mb();
-       /*
-        * Ensure that we must observe the pi_state in exit_pi_state_list().
+        * Switch the state to FUTEX_STATE_EXITING under tsk->pi_lock.
+        *
+        * This ensures that all subsequent checks of tsk->futex_state in
+        * attach_to_pi_owner() must observe FUTEX_STATE_EXITING with
+        * tsk->pi_lock held.
+        *
+        * It guarantees also that a pi_state which was queued right before
+        * the state change under tsk->pi_lock by a concurrent waiter must
+        * be observed in exit_pi_state_list().
         */
        raw_spin_lock_irq(&tsk->pi_lock);
+       tsk->futex_state = FUTEX_STATE_EXITING;
        raw_spin_unlock_irq(&tsk->pi_lock);
+}
+
+static void futex_cleanup_end(struct task_struct *tsk, int state)
+{
+       /*
+        * Lockless store. The only side effect is that an observer might
+        * take another loop until it becomes visible.
+        */
+       tsk->futex_state = state;
+}
 
-       futex_exec_release(tsk);
+void futex_exec_release(struct task_struct *tsk)
+{
+       /*
+        * The state handling is done for consistency, but in the case of
+        * exec() there is no way to prevent futher damage as the PID stays
+        * the same. But for the unlikely and arguably buggy case that a
+        * futex is held on exec(), this provides at least as much state
+        * consistency protection which is possible.
+        */
+       futex_cleanup_begin(tsk);
+       futex_cleanup(tsk);
+       /*
+        * Reset the state to FUTEX_STATE_OK. The task is alive and about
+        * exec a new binary.
+        */
+       futex_cleanup_end(tsk, FUTEX_STATE_OK);
+}
 
-       tsk->futex_state = FUTEX_STATE_DEAD;
+void futex_exit_release(struct task_struct *tsk)
+{
+       futex_cleanup_begin(tsk);
+       futex_cleanup(tsk);
+       futex_cleanup_end(tsk, FUTEX_STATE_DEAD);
 }
 
 long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,