Skip to content

Commit

Permalink
Merge branch 'exec-update-lock-for-v5.11' of git://git.kernel.org/pub…
Browse files Browse the repository at this point in the history
…/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
  • Loading branch information
torvalds committed Dec 16, 2020
2 parents faf145d + f7cfd87 commit d01e7f1
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 43 deletions.
12 changes: 6 additions & 6 deletions fs/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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;

Expand All @@ -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;
}
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
10 changes: 5 additions & 5 deletions fs/proc/base.c
Original file line number Diff line number Diff line change
Expand Up @@ -405,19 +405,19 @@ static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,

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;
}

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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}

Expand Down
11 changes: 6 additions & 5 deletions include/linux/sched/signal.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,13 @@ struct signal_struct {
* credential calculations
* (notably. ptrace)
* Deprecated do not use in new code.
* Use exec_update_mutex instead.
*/
struct mutex exec_update_mutex; /* Held while task_struct is being
* updated during exec, and may have
* inconsistent permissions.
* Use exec_update_lock instead.
*/
struct rw_semaphore exec_update_lock; /* Held while task_struct is
* being updated during exec,
* and may have inconsistent
* permissions.
*/
} __randomize_layout;

/*
Expand Down
2 changes: 1 addition & 1 deletion init/init_task.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
12 changes: 6 additions & 6 deletions kernel/events/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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).
Expand Down Expand Up @@ -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);
}

Expand All @@ -12153,7 +12153,7 @@ SYSCALL_DEFINE5(perf_event_open,
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:
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions kernel/fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -1225,7 +1225,7 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)
struct mm_struct *mm;
int err;

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

Expand All @@ -1235,7 +1235,7 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)
mmput(mm);
mm = ERR_PTR(-EACCES);
}
mutex_unlock(&task->signal->exec_update_mutex);
up_read(&task->signal->exec_update_lock);

return mm;
}
Expand Down Expand Up @@ -1595,7 +1595,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
sig->oom_score_adj_min = current->signal->oom_score_adj_min;

mutex_init(&sig->cred_guard_mutex);
mutex_init(&sig->exec_update_mutex);
init_rwsem(&sig->exec_update_lock);

return 0;
}
Expand Down
30 changes: 15 additions & 15 deletions kernel/kcmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,25 +70,25 @@ get_file_raw_ptr(struct task_struct *task, unsigned int idx)
return file;
}

static void kcmp_unlock(struct mutex *m1, struct mutex *m2)
static void kcmp_unlock(struct rw_semaphore *l1, struct rw_semaphore *l2)
{
if (likely(m2 != m1))
mutex_unlock(m2);
mutex_unlock(m1);
if (likely(l2 != l1))
up_read(l2);
up_read(l1);
}

static int kcmp_lock(struct mutex *m1, struct mutex *m2)
static int kcmp_lock(struct rw_semaphore *l1, struct rw_semaphore *l2)
{
int err;

if (m2 > m1)
swap(m1, m2);
if (l2 > l1)
swap(l1, l2);

err = mutex_lock_killable(m1);
if (!err && likely(m1 != m2)) {
err = mutex_lock_killable_nested(m2, SINGLE_DEPTH_NESTING);
err = down_read_killable(l1);
if (!err && likely(l1 != l2)) {
err = down_read_killable_nested(l2, SINGLE_DEPTH_NESTING);
if (err)
mutex_unlock(m1);
up_read(l1);
}

return err;
Expand Down Expand Up @@ -156,8 +156,8 @@ SYSCALL_DEFINE5(kcmp, pid_t, pid1, pid_t, pid2, int, type,
/*
* One should have enough rights to inspect task details.
*/
ret = kcmp_lock(&task1->signal->exec_update_mutex,
&task2->signal->exec_update_mutex);
ret = kcmp_lock(&task1->signal->exec_update_lock,
&task2->signal->exec_update_lock);
if (ret)
goto err;
if (!ptrace_may_access(task1, PTRACE_MODE_READ_REALCREDS) ||
Expand Down Expand Up @@ -212,8 +212,8 @@ SYSCALL_DEFINE5(kcmp, pid_t, pid1, pid_t, pid2, int, type,
}

err_unlock:
kcmp_unlock(&task1->signal->exec_update_mutex,
&task2->signal->exec_update_mutex);
kcmp_unlock(&task1->signal->exec_update_lock,
&task2->signal->exec_update_lock);
err:
put_task_struct(task1);
put_task_struct(task2);
Expand Down
4 changes: 2 additions & 2 deletions kernel/pid.c
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@ static struct file *__pidfd_fget(struct task_struct *task, int fd)
struct file *file;
int ret;

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

Expand All @@ -637,7 +637,7 @@ static struct file *__pidfd_fget(struct task_struct *task, int fd)
else
file = ERR_PTR(-EPERM);

mutex_unlock(&task->signal->exec_update_mutex);
up_read(&task->signal->exec_update_lock);

return file ?: ERR_PTR(-EBADF);
}
Expand Down

0 comments on commit d01e7f1

Please sign in to comment.