Commit d01e7f10 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'exec-update-lock-for-v5.11' of...

Merge branch 'exec-update-lock-for-v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace

Pull exec-update-lock update from Eric Biederman:
 "The key point of this is to transform exec_update_mutex into a
  rw_semaphore so readers can be separated from writers.

  This makes it easier to understand what the holders of the lock are
  doing, and makes it harder to contend or deadlock on the lock.

  The real deadlock fix wound up in perf_event_open"

* 'exec-update-lock-for-v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
  exec: Transform exec_update_mutex into a rw_semaphore
parents faf145d6 f7cfd871
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -966,8 +966,8 @@ EXPORT_SYMBOL(read_code);

/*
 * Maps the mm_struct mm into the current task struct.
 * On success, this function returns with the mutex
 * exec_update_mutex locked.
 * On success, this function returns with exec_update_lock
 * held for writing.
 */
static int exec_mmap(struct mm_struct *mm)
{
@@ -982,7 +982,7 @@ static int exec_mmap(struct mm_struct *mm)
	if (old_mm)
		sync_mm_rss(old_mm);

	ret = mutex_lock_killable(&tsk->signal->exec_update_mutex);
	ret = down_write_killable(&tsk->signal->exec_update_lock);
	if (ret)
		return ret;

@@ -996,7 +996,7 @@ static int exec_mmap(struct mm_struct *mm)
		mmap_read_lock(old_mm);
		if (unlikely(old_mm->core_state)) {
			mmap_read_unlock(old_mm);
			mutex_unlock(&tsk->signal->exec_update_mutex);
			up_write(&tsk->signal->exec_update_lock);
			return -EINTR;
		}
	}
@@ -1395,7 +1395,7 @@ int begin_new_exec(struct linux_binprm * bprm)
	return 0;

out_unlock:
	mutex_unlock(&me->signal->exec_update_mutex);
	up_write(&me->signal->exec_update_lock);
out:
	return retval;
}
@@ -1436,7 +1436,7 @@ void setup_new_exec(struct linux_binprm * bprm)
	 * some architectures like powerpc
	 */
	me->mm->task_size = TASK_SIZE;
	mutex_unlock(&me->signal->exec_update_mutex);
	up_write(&me->signal->exec_update_lock);
	mutex_unlock(&me->signal->cred_guard_mutex);
}
EXPORT_SYMBOL(setup_new_exec);
+5 −5
Original line number Diff line number Diff line
@@ -405,11 +405,11 @@ print0:

static int lock_trace(struct task_struct *task)
{
	int err = mutex_lock_killable(&task->signal->exec_update_mutex);
	int err = down_read_killable(&task->signal->exec_update_lock);
	if (err)
		return err;
	if (!ptrace_may_access(task, PTRACE_MODE_ATTACH_FSCREDS)) {
		mutex_unlock(&task->signal->exec_update_mutex);
		up_read(&task->signal->exec_update_lock);
		return -EPERM;
	}
	return 0;
@@ -417,7 +417,7 @@ static int lock_trace(struct task_struct *task)

static void unlock_trace(struct task_struct *task)
{
	mutex_unlock(&task->signal->exec_update_mutex);
	up_read(&task->signal->exec_update_lock);
}

#ifdef CONFIG_STACKTRACE
@@ -2930,7 +2930,7 @@ static int do_io_accounting(struct task_struct *task, struct seq_file *m, int wh
	unsigned long flags;
	int result;

	result = mutex_lock_killable(&task->signal->exec_update_mutex);
	result = down_read_killable(&task->signal->exec_update_lock);
	if (result)
		return result;

@@ -2966,7 +2966,7 @@ static int do_io_accounting(struct task_struct *task, struct seq_file *m, int wh
	result = 0;

out_unlock:
	mutex_unlock(&task->signal->exec_update_mutex);
	up_read(&task->signal->exec_update_lock);
	return result;
}

+6 −5
Original line number Diff line number Diff line
@@ -228,11 +228,12 @@ struct signal_struct {
					 * credential calculations
					 * (notably. ptrace)
					 * Deprecated do not use in new code.
					 * Use exec_update_mutex instead.
					 * Use exec_update_lock instead.
					 */
	struct mutex exec_update_mutex;	/* Held while task_struct is being
					 * updated during exec, and may have
					 * inconsistent permissions.
	struct rw_semaphore exec_update_lock;	/* Held while task_struct is
						 * being updated during exec,
						 * and may have inconsistent
						 * permissions.
						 */
} __randomize_layout;

+1 −1
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ static struct signal_struct init_signals = {
	.multiprocess	= HLIST_HEAD_INIT,
	.rlim		= INIT_RLIMITS,
	.cred_guard_mutex = __MUTEX_INITIALIZER(init_signals.cred_guard_mutex),
	.exec_update_mutex = __MUTEX_INITIALIZER(init_signals.exec_update_mutex),
	.exec_update_lock = __RWSEM_INITIALIZER(init_signals.exec_update_lock),
#ifdef CONFIG_POSIX_TIMERS
	.posix_timers = LIST_HEAD_INIT(init_signals.posix_timers),
	.cputimer	= {
+6 −6
Original line number Diff line number Diff line
@@ -1327,7 +1327,7 @@ static void put_ctx(struct perf_event_context *ctx)
 * function.
 *
 * Lock order:
 *    exec_update_mutex
 *    exec_update_lock
 *	task_struct::perf_event_mutex
 *	  perf_event_context::mutex
 *	    perf_event::child_mutex;
@@ -11959,14 +11959,14 @@ SYSCALL_DEFINE5(perf_event_open,
	}

	if (task) {
		err = mutex_lock_interruptible(&task->signal->exec_update_mutex);
		err = down_read_interruptible(&task->signal->exec_update_lock);
		if (err)
			goto err_file;

		/*
		 * Preserve ptrace permission check for backwards compatibility.
		 *
		 * We must hold exec_update_mutex across this and any potential
		 * We must hold exec_update_lock across this and any potential
		 * perf_install_in_context() call for this new event to
		 * serialize against exec() altering our credentials (and the
		 * perf_event_exit_task() that could imply).
@@ -12129,7 +12129,7 @@ SYSCALL_DEFINE5(perf_event_open,
	mutex_unlock(&ctx->mutex);

	if (task) {
		mutex_unlock(&task->signal->exec_update_mutex);
		up_read(&task->signal->exec_update_lock);
		put_task_struct(task);
	}

@@ -12153,7 +12153,7 @@ err_locked:
	mutex_unlock(&ctx->mutex);
err_cred:
	if (task)
		mutex_unlock(&task->signal->exec_update_mutex);
		up_read(&task->signal->exec_update_lock);
err_file:
	fput(event_file);
err_context:
@@ -12470,7 +12470,7 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
/*
 * When a child task exits, feed back event values to parent events.
 *
 * Can be called with exec_update_mutex held when called from
 * Can be called with exec_update_lock held when called from
 * setup_new_exec().
 */
void perf_event_exit_task(struct task_struct *child)
Loading