Patch "perf/core: Fix concurrent sys_perf_event_open() vs. 'move_group' race" has been added to the 4.9-stable tree
Subsystems (2)
This is a note to let you know that I've just added the patch titled
perf/core: Fix concurrent sys_perf_event_open() vs. 'move_group' race
to the 4.9-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
perf-core-fix-concurrent-sys_perf_event_open-vs.-move_group-race.patch
and it can be found in the queue-4.9 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
321027c1fe77f892f4ea07846aeae08cefbbb290Mon Sep 17 00:00:00 2001
From: Peter Zijlstra <[email protected]>
Date: Wed, 11 Jan 2017 21:09:50 +0100
Subject: perf/core: Fix concurrent sys_perf_event_open() vs. 'move_group' race
From: Peter Zijlstra <[email protected]>
commit 321027c1fe77f892f4ea07846aeae08cefbbb290 upstream.
Di Shen reported a race between two concurrent sys_perf_event_open() calls where both try and move the same pre-existing software group into a hardware context.
The problem is exactly that described in commit:
f63a8daa5812 ("perf: Fix event->ctx locking")
... where, while we wait for a ctx->mutex acquisition, the event->ctx relation can have changed under us.
That very same commit failed to recognise sys_perf_event_context() as an external access vector to the events and thereby didn't apply the established locking rules correctly.
So while one sys_perf_event_open() call is stuck waiting on mutex_lock_double(), the other (which owns said locks) moves the group about. So by the time the former sys_perf_event_open() acquires the locks, the context we've acquired is stale (and possibly dead).
Apply the established locking rules as per perf_event_ctx_lock_nested() to the mutex_lock_double() for the 'move_group' case. This obviously means we need to validate state after we acquire the locks.
--- kernel/events/core.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 4 deletions(-)
kernel/events/core.c +54 -4
@@ -9503,6 +9503,37 @@ static int perf_event_set_clock(struct preturn 0 ; } +/* +* Variation on perf_event_ctx_lock_nested(), except we take two context +* mutexes. +*/ +static struct perf_event_context * +__perf_event_ctx_lock_double (struct perf_event *group_leader, +struct perf_event_context *ctx) +{ +struct perf_event_context *gctx; + +again: +rcu_read_lock (); + gctx =READ_ONCE (group_leader->ctx); +if (!atomic_inc_not_zero (&gctx->refcount)) { +rcu_read_unlock (); + goto again; + } +rcu_read_unlock (); + +mutex_lock_double (&gctx->mutex, &ctx->mutex); + +if (group_leader->ctx != gctx) { +mutex_unlock (&ctx->mutex); +mutex_unlock (&gctx->mutex); +put_ctx (gctx); + goto again; + } + +return gctx; +} +/** * sys_perf_event_open - open a performance event, associate it to a task/cpu *
@@ -9746,12 +9777,31 @@ SYSCALL_DEFINE5(perf_event_open, }if (move_group) { - gctx = group_leader->ctx; -mutex_lock_double (&gctx->mutex, &ctx->mutex); + gctx =__perf_event_ctx_lock_double (group_leader, ctx); +if (gctx->task ==TASK_TOMBSTONE ) { err = -ESRCH ; goto err_locked; } + +/* +* Check if we raced against another sys_perf_event_open() call +* moving the software group underneath us. +*/ +if (!(group_leader->group_caps &PERF_EV_CAP_SOFTWARE )) { +/* +* If someone moved the group out from under us, check +* if this new event wound up on the same ctx, if so +* its the regular !move_group case, otherwise fail. +*/ +if (gctx != ctx) { + err = -EINVAL ; + goto err_locked; + }else { +perf_event_ctx_unlock (group_leader, gctx); + move_group =0 ; + } + } }else {mutex_lock (&ctx->mutex); }
@@ -9853,7 +9903,7 @@ SYSCALL_DEFINE5(perf_event_open,perf_unpin_context (ctx);if (move_group) -mutex_unlock (&gctx->mutex); +perf_event_ctx_unlock (group_leader, gctx);mutex_unlock (&ctx->mutex);if (task) {
@@ -9879,7 +9929,7 @@ SYSCALL_DEFINE5(perf_event_open, err_locked:if (move_group) -mutex_unlock (&gctx->mutex); +perf_event_ctx_unlock (group_leader, gctx);mutex_unlock (&ctx->mutex);/* err_file: */ fput (event_file);
Patches currently in stable-queue which might be from [email protected] are
queue-4.9/perf-core-fix-concurrent-sys_perf_event_open-vs.-move_group-race.patch