Skip to content

Commit af8cbda

Browse files
committed
futex: Provide state handling for exec() as well
exec() attempts to handle potentially held futexes gracefully by running the futex exit handling code like exit() does. The current implementation has no protection against concurrent incoming waiters. The reason is that the futex state cannot be set to FUTEX_STATE_DEAD after the cleanup because the task struct is still active and just about to execute the new binary. While its arguably buggy when a task holds a futex over exec(), for consistency sake the state handling can at least cover the actual futex exit cleanup section. This provides state consistency protection accross the cleanup. As the futex state of the task becomes FUTEX_STATE_OK after the cleanup has been finished, this cannot prevent subsequent attempts to attach to the task in case that the cleanup was not successfull in mopping up all leftovers. Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-by: Ingo Molnar <[email protected]> Acked-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent 4a8e991 commit af8cbda

File tree

1 file changed

+34
-4
lines changed

1 file changed

+34
-4
lines changed

kernel/futex.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3661,7 +3661,7 @@ static void exit_robust_list(struct task_struct *curr)
36613661
}
36623662
}
36633663

3664-
void futex_exec_release(struct task_struct *tsk)
3664+
static void futex_cleanup(struct task_struct *tsk)
36653665
{
36663666
if (unlikely(tsk->robust_list)) {
36673667
exit_robust_list(tsk);
@@ -3701,7 +3701,7 @@ void futex_exit_recursive(struct task_struct *tsk)
37013701
tsk->futex_state = FUTEX_STATE_DEAD;
37023702
}
37033703

3704-
void futex_exit_release(struct task_struct *tsk)
3704+
static void futex_cleanup_begin(struct task_struct *tsk)
37053705
{
37063706
/*
37073707
* Switch the state to FUTEX_STATE_EXITING under tsk->pi_lock.
@@ -3717,10 +3717,40 @@ void futex_exit_release(struct task_struct *tsk)
37173717
raw_spin_lock_irq(&tsk->pi_lock);
37183718
tsk->futex_state = FUTEX_STATE_EXITING;
37193719
raw_spin_unlock_irq(&tsk->pi_lock);
3720+
}
37203721

3721-
futex_exec_release(tsk);
3722+
static void futex_cleanup_end(struct task_struct *tsk, int state)
3723+
{
3724+
/*
3725+
* Lockless store. The only side effect is that an observer might
3726+
* take another loop until it becomes visible.
3727+
*/
3728+
tsk->futex_state = state;
3729+
}
37223730

3723-
tsk->futex_state = FUTEX_STATE_DEAD;
3731+
void futex_exec_release(struct task_struct *tsk)
3732+
{
3733+
/*
3734+
* The state handling is done for consistency, but in the case of
3735+
* exec() there is no way to prevent futher damage as the PID stays
3736+
* the same. But for the unlikely and arguably buggy case that a
3737+
* futex is held on exec(), this provides at least as much state
3738+
* consistency protection which is possible.
3739+
*/
3740+
futex_cleanup_begin(tsk);
3741+
futex_cleanup(tsk);
3742+
/*
3743+
* Reset the state to FUTEX_STATE_OK. The task is alive and about
3744+
* exec a new binary.
3745+
*/
3746+
futex_cleanup_end(tsk, FUTEX_STATE_OK);
3747+
}
3748+
3749+
void futex_exit_release(struct task_struct *tsk)
3750+
{
3751+
futex_cleanup_begin(tsk);
3752+
futex_cleanup(tsk);
3753+
futex_cleanup_end(tsk, FUTEX_STATE_DEAD);
37243754
}
37253755

37263756
long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,

0 commit comments

Comments
 (0)