Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e56fb287 authored by Oleg Nesterov's avatar Oleg Nesterov Committed by Linus Torvalds
Browse files

exec: do not abuse ->cred_guard_mutex in threadgroup_lock()



threadgroup_lock() takes signal->cred_guard_mutex to ensure that
thread_group_leader() is stable.  This doesn't look nice, the scope of
this lock in do_execve() is huge.

And as Dave pointed out this can lead to deadlock, we have the
following dependencies:

	do_execve:		cred_guard_mutex -> i_mutex
	cgroup_mount:		i_mutex -> cgroup_mutex
	attach_task_by_pid:	cgroup_mutex -> cred_guard_mutex

Change de_thread() to take threadgroup_change_begin() around the
switch-the-leader code and change threadgroup_lock() to avoid
->cred_guard_mutex.

Note that de_thread() can't sleep with ->group_rwsem held, this can
obviously deadlock with the exiting leader if the writer is active, so it
does threadgroup_change_end() before schedule().

Reported-by: default avatarDave Jones <davej@redhat.com>
Acked-by: default avatarTejun Heo <tj@kernel.org>
Acked-by: default avatarLi Zefan <lizefan@huawei.com>
Signed-off-by: default avatarOleg Nesterov <oleg@redhat.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 12eaaf30
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -898,11 +898,13 @@ static int de_thread(struct task_struct *tsk)

		sig->notify_count = -1;	/* for exit_notify() */
		for (;;) {
			threadgroup_change_begin(tsk);
			write_lock_irq(&tasklist_lock);
			if (likely(leader->exit_state))
				break;
			__set_current_state(TASK_KILLABLE);
			write_unlock_irq(&tasklist_lock);
			threadgroup_change_end(tsk);
			schedule();
			if (unlikely(__fatal_signal_pending(tsk)))
				goto killed;
@@ -960,6 +962,7 @@ static int de_thread(struct task_struct *tsk)
		if (unlikely(leader->ptrace))
			__wake_up_parent(leader, leader->parent);
		write_unlock_irq(&tasklist_lock);
		threadgroup_change_end(tsk);

		release_task(leader);
	}
+4 −14
Original line number Diff line number Diff line
@@ -2249,27 +2249,18 @@ static inline void threadgroup_change_end(struct task_struct *tsk)
 *
 * Lock the threadgroup @tsk belongs to.  No new task is allowed to enter
 * and member tasks aren't allowed to exit (as indicated by PF_EXITING) or
 * perform exec.  This is useful for cases where the threadgroup needs to
 * stay stable across blockable operations.
 * change ->group_leader/pid.  This is useful for cases where the threadgroup
 * needs to stay stable across blockable operations.
 *
 * fork and exit paths explicitly call threadgroup_change_{begin|end}() for
 * synchronization.  While held, no new task will be added to threadgroup
 * and no existing live task will have its PF_EXITING set.
 *
 * During exec, a task goes and puts its thread group through unusual
 * changes.  After de-threading, exclusive access is assumed to resources
 * which are usually shared by tasks in the same group - e.g. sighand may
 * be replaced with a new one.  Also, the exec'ing task takes over group
 * leader role including its pid.  Exclude these changes while locked by
 * grabbing cred_guard_mutex which is used to synchronize exec path.
 * de_thread() does threadgroup_change_{begin|end}() when a non-leader
 * sub-thread becomes a new leader.
 */
static inline void threadgroup_lock(struct task_struct *tsk)
{
	/*
	 * exec uses exit for de-threading nesting group_rwsem inside
	 * cred_guard_mutex. Grab cred_guard_mutex first.
	 */
	mutex_lock(&tsk->signal->cred_guard_mutex);
	down_write(&tsk->signal->group_rwsem);
}

@@ -2282,7 +2273,6 @@ static inline void threadgroup_lock(struct task_struct *tsk)
static inline void threadgroup_unlock(struct task_struct *tsk)
{
	up_write(&tsk->signal->group_rwsem);
	mutex_unlock(&tsk->signal->cred_guard_mutex);
}
#else
static inline void threadgroup_change_begin(struct task_struct *tsk) {}