Patch "perf/core: Fix concurrent sys_perf_event_open() vs. 'move_group' race" has been added to the 4.4-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.4-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.4 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.
[bwh: Backported to 4.4:
- Test perf_event::group_flags instead of group_caps
- Adjust context]
--- kernel/events/core.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-)
kernel/events/core.c +53 -4
@@ -8250,6 +8250,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 *
@@ -8486,8 +8517,26 @@ 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); + +/* +* Check if we raced against another sys_perf_event_open() call +* moving the software group underneath us. +*/ +if (!(group_leader->group_flags &PERF_GROUP_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); }
@@ -8582,7 +8631,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) {
@@ -8610,7 +8659,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.4/perf-core-fix-concurrent-sys_perf_event_open-vs.-move_group-race.patch