Commit c5174678 authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Greg Kroah-Hartman

perf/core: Fix perf_event_open() vs. execve() race

commit 79c9ce57 upstream.

Jann reported that the ptrace_may_access() check in
find_lively_task_by_vpid() is racy against exec().

Specifically:

  perf_event_open()		execve()

  ptrace_may_access()
				commit_creds()
  ...				if (get_dumpable() != SUID_DUMP_USER)
				  perf_event_exit_task();
  perf_install_in_context()

would result in installing a counter across the creds boundary.

Fix this by wrapping lots of perf_event_open() in cred_guard_mutex.
This should be fine as perf_event_exit_task() is already called with
cred_guard_mutex held, so all perf locks already nest inside it.
Reported-by: default avatarJann Horn <jannh@google.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vince Weaver <vincent.weaver@maine.edu>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
Signed-off-by: default avatarHe Kuang <hekuang@huawei.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a7a9e0ef
...@@ -946,6 +946,7 @@ static void put_ctx(struct perf_event_context *ctx) ...@@ -946,6 +946,7 @@ static void put_ctx(struct perf_event_context *ctx)
* function. * function.
* *
* Lock order: * Lock order:
* cred_guard_mutex
* task_struct::perf_event_mutex * task_struct::perf_event_mutex
* perf_event_context::mutex * perf_event_context::mutex
* perf_event_context::lock * perf_event_context::lock
...@@ -3418,7 +3419,6 @@ static struct task_struct * ...@@ -3418,7 +3419,6 @@ static struct task_struct *
find_lively_task_by_vpid(pid_t vpid) find_lively_task_by_vpid(pid_t vpid)
{ {
struct task_struct *task; struct task_struct *task;
int err;
rcu_read_lock(); rcu_read_lock();
if (!vpid) if (!vpid)
...@@ -3432,16 +3432,7 @@ find_lively_task_by_vpid(pid_t vpid) ...@@ -3432,16 +3432,7 @@ find_lively_task_by_vpid(pid_t vpid)
if (!task) if (!task)
return ERR_PTR(-ESRCH); return ERR_PTR(-ESRCH);
/* Reuse ptrace permission checks for now. */
err = -EACCES;
if (!ptrace_may_access(task, PTRACE_MODE_READ_REALCREDS))
goto errout;
return task; return task;
errout:
put_task_struct(task);
return ERR_PTR(err);
} }
/* /*
...@@ -8328,6 +8319,24 @@ SYSCALL_DEFINE5(perf_event_open, ...@@ -8328,6 +8319,24 @@ SYSCALL_DEFINE5(perf_event_open,
get_online_cpus(); get_online_cpus();
if (task) {
err = mutex_lock_interruptible(&task->signal->cred_guard_mutex);
if (err)
goto err_cpus;
/*
* Reuse ptrace permission checks for now.
*
* We must hold cred_guard_mutex 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).
*/
err = -EACCES;
if (!ptrace_may_access(task, PTRACE_MODE_READ_REALCREDS))
goto err_cred;
}
if (flags & PERF_FLAG_PID_CGROUP) if (flags & PERF_FLAG_PID_CGROUP)
cgroup_fd = pid; cgroup_fd = pid;
...@@ -8335,7 +8344,7 @@ SYSCALL_DEFINE5(perf_event_open, ...@@ -8335,7 +8344,7 @@ SYSCALL_DEFINE5(perf_event_open,
NULL, NULL, cgroup_fd); NULL, NULL, cgroup_fd);
if (IS_ERR(event)) { if (IS_ERR(event)) {
err = PTR_ERR(event); err = PTR_ERR(event);
goto err_cpus; goto err_cred;
} }
if (is_sampling_event(event)) { if (is_sampling_event(event)) {
...@@ -8394,11 +8403,6 @@ SYSCALL_DEFINE5(perf_event_open, ...@@ -8394,11 +8403,6 @@ SYSCALL_DEFINE5(perf_event_open,
goto err_context; goto err_context;
} }
if (task) {
put_task_struct(task);
task = NULL;
}
/* /*
* Look up the group leader (we will attach this event to it): * Look up the group leader (we will attach this event to it):
*/ */
...@@ -8486,6 +8490,11 @@ SYSCALL_DEFINE5(perf_event_open, ...@@ -8486,6 +8490,11 @@ SYSCALL_DEFINE5(perf_event_open,
WARN_ON_ONCE(ctx->parent_ctx); WARN_ON_ONCE(ctx->parent_ctx);
/*
* This is the point on no return; we cannot fail hereafter. This is
* where we start modifying current state.
*/
if (move_group) { if (move_group) {
/* /*
* See perf_event_ctx_lock() for comments on the details * See perf_event_ctx_lock() for comments on the details
...@@ -8555,6 +8564,11 @@ SYSCALL_DEFINE5(perf_event_open, ...@@ -8555,6 +8564,11 @@ SYSCALL_DEFINE5(perf_event_open,
mutex_unlock(&gctx->mutex); mutex_unlock(&gctx->mutex);
mutex_unlock(&ctx->mutex); mutex_unlock(&ctx->mutex);
if (task) {
mutex_unlock(&task->signal->cred_guard_mutex);
put_task_struct(task);
}
put_online_cpus(); put_online_cpus();
event->owner = current; event->owner = current;
...@@ -8589,6 +8603,9 @@ SYSCALL_DEFINE5(perf_event_open, ...@@ -8589,6 +8603,9 @@ SYSCALL_DEFINE5(perf_event_open,
*/ */
if (!event_file) if (!event_file)
free_event(event); free_event(event);
err_cred:
if (task)
mutex_unlock(&task->signal->cred_guard_mutex);
err_cpus: err_cpus:
put_online_cpus(); put_online_cpus();
err_task: err_task:
...@@ -8868,6 +8885,9 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn) ...@@ -8868,6 +8885,9 @@ 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. * When a child task exits, feed back event values to parent events.
*
* Can be called with cred_guard_mutex held when called from
* install_exec_creds().
*/ */
void perf_event_exit_task(struct task_struct *child) void perf_event_exit_task(struct task_struct *child)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment